Skip to main content
PathDocs

Boot & Configuration

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:

  • Only web and headless come with built-in templates (auto-initialized, default-mount @deepseek-ai/dsh-base); tui etc. require a self-built directory
  • initProfile creates: directory + package.json (with dsh.profile.bundles) + empty cordis.patch.yml + pnpm-workspace.yaml
  • healProfilesModuleFallback maintains the flat symlink fallback $DSH_HOME/profiles/node_modules, so out-of-tree plugins resolve to the same cordis
  • A home-directory-level cordis.patch.yml ($DSH_HOME/cordis.patch.yml) overrides the per-profile layer

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 layers)

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).

6. Verification

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

Next steps