Skip to main content
PathDocs

Boot & Configuration

Audit baseline 0.1.5-alpha.1 @ 5dda764ed3.

One-liner: dsh's startup = the app-boot glue layer that assembles "environment → profile → bundle layers → your patch layers" into one config tree; the Loader loads, asserts, activates, and fails loudly on failure (dsh:-prefixed exit).

dsh web / dsh --profile <name> both run through the same boot. Understand it and you'll know "which layer a change lands in, which layer overrides which, and how to inspect the config tree".

1. Startup flow (boot glue layer)​

2. Layered environment variables​

loadLayeredEnv priority:

inherited env > project .env > user .env
  • bootstrap-only variables from files are rejected
  • file values are materialized without replacing inherited values

3. Profile mechanism​

FunctionResponsibility
resolveProfileDir / initProfileLocate / initialize $DSH_HOME/profiles/<name>
readProfileManifestRead the profile's dsh.profile.bundles list
composeEntriesCombine the bundle layers into entries
loadOptionalPatchesParse cordis.patch.yml (top-level YAML: insert / override / !!js)
watchUserPatchesHMR: transactionally re-compose on patch change

Key details:

  • 5 built-in templates (auto-initialized on first use); tui etc. require a self-built directory
Templatedsh.profile.bundlespatchReload
acp@deepseek-ai/dsh-base + @deepseek-ai/dsh-acp-appstartup
web@deepseek-ai/dsh-base + @deepseek-ai/dsh-web-applive
headless@deepseek-ai/dsh-base + @deepseek-ai/dsh-headlessstartup
sdk@deepseek-ai/dsh-base + @deepseek-ai/dsh-sdk-appstartup
sdk-minimal@deepseek-ai/dsh-sdk-minimalstartup
  • A self-built profile absent from the table defaults to patchReload: live; dsh --profile <new-name> --from-default-profile <template> copies a new profile from a template
  • initProfile creates: directory + package.json (with dsh.profile.bundles and patchReload) + a commented cordis.patch.yml template + pnpm-workspace.yaml (nodeLinker: hoisted)
  • healProfilesModuleFallback maintains the $DSH_HOME/profiles/node_modules installation-dependency-closure fallback (symlinks under plain Node, ESM proxies for a packaged executable), so out-of-tree plugins resolve to the same cordis; bundle-private dependencies are linked through the profile's own node_modules
  • A home-directory-level cordis.patch.yml ($DSH_HOME/cordis.patch.yml) overrides the per-profile layer; the application order is bundle layers → profile patch → home patch → --patch overlays → telemetry switch

See the complete patch application order in Plugin Anatomy.

4. Fail-loud behavior​

When $DSH_SNAPSHOT === 'replay', resolveConfigPath swaps cordis.yml for cordis.snapshot.yml (snapshot replay). On load failure:

$ dsh web # on plugin resolution failure
dsh: plugin tree failed to load: ...
# or
dsh: fatal load failure: <Error stack>
# The tag prefix is always 'dsh:', the process exits(1), it never runs while broken

The two assertion functions differ:

AssertionWhat it checks
assertEntriesLoadedAfter the tree settles, entries that exist as enabled but have no fiber → throw
assertEntriesActivatedThen waits for each enabled entry to activate; errors carry the raw stack

5. Inspecting configuration: --dump-config​

dsh web --dump-config # renders the current composition tree (with '# == ' layer-source comments)
dsh web --dump-default-config # renders only the bundle layers (no user layer, no --patch overlays)
dsh web --patch ./extra.yml # temporarily overlays one patch layer (repeatable, applied after the profile layer)

--dump-default-config is the recovery diagnostic for a broken user layer: it does not parse cordis.patch.yml. renderConfigDump synthesizes the tree offline using the Loader's own parser, so the result matches real startup: this is the authoritative tool for troubleshooting "which layer overrode what". Each line's # == comment marks which layer it came from (base / web-app / your profile / a --patch file).

6. Verification​

dsh web --dump-config | head -30 # inspect the layered structure and source comments

Next steps​