Boot & Configuration
One-liner:
dsh's startup = theapp-bootglue 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
| Function | Responsibility |
|---|---|
resolveProfileDir / initProfile | Locate / initialize $DSH_HOME/profiles/<name> |
readProfileManifest | Read the profile's dsh.profile.bundles list |
composeEntries | Combine the bundle layers into entries |
loadOptionalPatches | Parse cordis.patch.yml (top-level YAML: insert / override / !!js) |
watchUserPatches | HMR: transactionally re-compose on patch change |
Key details:
- Only
webandheadlesscome with built-in templates (auto-initialized, default-mount@deepseek-ai/dsh-base);tuietc. require a self-built directory initProfilecreates: directory + package.json (withdsh.profile.bundles) + emptycordis.patch.yml+pnpm-workspace.yamlhealProfilesModuleFallbackmaintains 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:
| Assertion | What it checks |
|---|---|
assertEntriesLoaded | After the tree settles, entries that exist as enabled but have no fiber → throw |
assertEntriesActivated | Then 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
- Context: what the model sees
- Agent Main Loop: how the loop drives
- Plugin Anatomy: how the patch layer works