Boot, Profiles, Bundles, and Configuration Layers
How a running dsh process is composed into an ordered plugin tree
Conclusion: the launcher evaluates a configuration program
The primary product of a dsh launch is not a hard-coded Application object. It is a plugin tree evaluated from an empty root, ordered patch layers, service dependencies, and Cordis lifecycle semantics.
This distinction matters. A Profile chooses bundles; bundles provide large capability sets; user and command-line overlays modify the result; Loader activates plugins as their required services become available. Configuration is not merely a collection of startup parameters here—it is the product architecture.
1. The CLI owns a deliberately narrow boundary
apps/cli/src/bin.ts parses argv into three modes and dynamically imports the corresponding implementation: normal profile launch, plugin package management, or configuration dump. The profile branch passes only a frozen environment snapshot, the profile name, --patch files in argv order, and remaining application arguments. CLI-PROFILE-DISPATCH
“CLI arguments” are intentionally split into two ownership classes:
| Input | Owner | How it enters the tree | Recomputed during live updates? |
|---|---|---|---|
--profile, --patch | Outer launcher | Select a profile and form patch layers | Patch contents are reread; the file list is fixed |
| Remaining arguments | Plugins inside the tree | Provided through cmdlineArgs, not encoded into patches | No; they live for this invocation |
| Environment | Launcher freezes provenance | Environment snapshot is provided before mounting; !!js may also read process environment | The launch-time snapshot is fixed |
Command-line product behavior does not invade generic boot. Web, headless, and future surfaces can mount their argument interpreters as ordinary providers. The launcher owns only “which configuration program” and “how the process terminates safely.”
2. A Profile is a writable composition workspace
A Profile name must be one path segment and cannot be ., .., or node_modules. On first use, the built-in web template initializes as base + web-app, while headless initializes as base + headless. Initialization creates only missing package.json, cordis.patch.yml, and pnpm-workspace.yaml files and never overwrites existing user data. PROFILE-TEMPLATES
| Profile file | Responsibility | Mutability and risk |
|---|---|---|
package.json | Declares the ordered dsh.profile.bundles list and profile-private plugin dependencies | Changes both the product capability set and module-resolution closure |
cordis.patch.yml | User override layer for this profile | Watched live; may configure, disable, or insert plugins |
pnpm-workspace.yaml | Fixes the hoisted linker and peer policy needed by out-of-tree plugins | Lets a profile install plugins while sharing Cordis and service-definition instances |
cordis.yml | A real include anchor required by Loader | Rewritten to an empty list on every launch; not user-maintained |
The launcher rewrites cordis.yml to an empty entry list on every launch. This prevents a Loader write-back of the current runtime tree from being combined again with bundle inserts on the next launch. The file exists only to anchor resolution at the Profile directory. PROFILE-ROOT
3. The exact six-level precedence stack
The final entry list starts empty and applies the following layers. Later layers have higher precedence:
Bundle patches
Concatenated strictly in dsh.profile.bundles manifest order—normally base followed by a surface bundle.
Profile user patch
$DSH_HOME/profiles/<name>/cordis.patch.yml, scoped to one Profile.
Home user patch
$DSH_HOME/cordis.patch.yml, a machine-level preference across Profiles, so it outranks the Profile layer.
CLI overlays
All --patch files in their argv order.
Launcher assembly overlay
If an agent-presets row exists, the launcher injects the read-only preset root shipped with the installation.
Telemetry hard-disable
Any non-empty DSH_TELEMETRY_DISABLED value adds a final overlay that disables the row. A privacy switch prefers accidental off over accidental on.
allPatches() flattens bundle, profile, home, and launcher overlays. composeProfile() first indexes rows from the initial user-selectable layers, then appends the preset and telemetry overrides whose semantics belong exclusively to the launcher. PROFILE-COMPOSE
Configuration dump, flag derivation, and real boot all share the same applyEntryPatches([], ...) semantics. A listed package without a dsh.bundle.patch declaration fails immediately instead of becoming a silent empty layer. PROFILE-LOADPROFILE-ORDER-TEST
4. Patch semantics: address a row, replace config wholesale
dsh-base performs one large insert over the empty root. Later patches address rows by id, with the last write winning. On a matching row, config is replaced wholesale rather than recursively deep-merged. File order does not encode load order; service availability drives activation. BASE-BUNDLE-SEMANTICS
| Operation | Effect | Frequent misreading |
|---|---|---|
{ id, config } | Replace the target row's complete config | Writing one field does not retain other base fields |
{ id, disabled } | Change whether the row may mount | Disable is not deletion; a later layer may explicitly override it |
{ insert: [...] } | Add new plugin rows | Insertion position helps readers but is not a dependency-order mechanism |
Target a missing id | Loader emits a skipped-patch warning | This permits cross-surface overlays, but a misspelled ID may also remain only a warning |
Wholesale replacement makes ownership and the final shape of one row deterministic, avoiding implicit rules for merging arrays, deleting fields, or combining expressions. The cost is repetition: an upper layer must restate the complete configuration. It fits small deployment overrides better than huge, fast-evolving nested objects exposed directly to ordinary users.
5. Module resolution: installation first, Profile extension second
Bundle resolution tries the dsh installation anchor before the Profile's own package.json. Before launch, a breadth-first traversal of the application's dependencies + peerDependencies closure creates flat links under $DSH_HOME/profiles/node_modules. Node's parent-directory fallback from a Profile-private node_modules can then find installation-owned packages. PROFILE-MODULE-FALLBACK
This produces two coexisting resolution worlds:
The flat fallback does more than make packages discoverable. It helps an out-of-tree plugin and official packages resolve the same Cordis and service-definition instances. If each side loads its own framework copy, types may look identical while runtime context keys and registry identity no longer meet.
6. Boot is a commit protocol, not best-effort mounting
boot() creates the root Context, provides the dsh home, installs Loader, and runs host prepare. It then mounts the root Include, waits for Loader settlement, and audits that every entry activated. Any failure first disposes the partial root and then throws a diagnostic containing the phase, wrapper chain, and deepest plugin stack. BOOT-TRANSACTION
| Stage | Visible state | Failure ownership |
|---|---|---|
| Context + Loader | No configuration-tree plugins exist yet | Reported as host preparation failure |
| Host prepare | Frozen environment and argv facts have been provided | A failure disposes the entire root |
| Include mount / settle | Plugins activate progressively as dependencies resolve | Transactional Loader wraps the failure; the root rolls back uniformly |
| Activation audit | Only a settled, live tree may return | Missing fibers and never-activating entries reject startup |
| Runtime | A signal or surface may dispose the root | Single-shot root-fiber disposal retracts effects |
This protocol separates “declared in configuration” from “actually available at runtime.” The commit point is Loader completion plus the activation audit. Partial-service startup flexibility is traded for refusing to serve from a silently half-mounted tree.
7. Live configuration preserves the last valid tree
When either Profile or home patch changes, the launcher rereads both user files in the same generation and recomposes them between fixed bundles and higher-priority overlays. Every generation receives a new deep clone so Include's in-place mutation of inserted rows cannot contaminate the next composition. The result is applied transactionally through the root Include's entry.update(). PROFILE-LIVEBOOT-HMR
An integration-level test writes a valid configuration, a plugin-startup failure, a syntax error, a recovered configuration, and finally removes the file. Both failures broadcast an error and retain the previous valid tree; recovery applies the new tree, and deletion restores the application-owned layer. PROFILE-HMR-TEST
The Web bundle currently disables shared module HMR, but the launcher does not let the configuration hot-reload contract disappear. If no HMR service exists, it mounts a timer and a watch-only HMR instance with no module roots solely for the two user patch files.
8. Base, Headless, and Web: one spine, different product planes
The base bundle directly assembles the shared spine: LLM registry, sessions, Typert API, Agent service, jobs, retry, settings, credentials, multiple provider adapters, and JSONL persistence. Later rows continue with tools, compaction, multi-Agent capabilities, and the loop. BASE-BUNDLE-SPINE
| Surface | Additions over base | Agent-capability ownership | Lifecycle |
|---|---|---|---|
| Headless | Code runtime, launch-argument provider, and one-shot runner; no Host, HTTP, Web, or browser | Process-level Agent plane from base | Requests bounded exit when the task finishes |
| Web | Host, storage, projection cache, HTTP/API, browser module roster, and UI | Disables per-Agent base rows and remounts them inside each session's preset realm | Long-lived, with concurrent sessions |
The headless bundle explicitly mounts no Web or HTTP surface. An ordinary provider interprets the positional task, and its runner creates an Agent through the core registry. HEADLESS-BUNDLE
Web disables base rows belonging to one Agent—tools, prompt contributors, compaction, and delegation—and rebuilds them through per-session presets. Cross-session registries or services read by host RPC, including jobs, goals, token metering, and subagent state, remain in the host plane. WEB-AGENT-PLANE
Whether a capability belongs in a preset cannot be inferred from its name. One must trace its readers, whether a registry spans sessions, whether provider names are globally unique, and which context a host RPC uses to resolve the service. The long Web-patch commentary is effectively a struct-lifetime audit: process state misplaced in session scope produces missing browser services, broken cross-session queries, or duplicate registration collisions.
9. Benefits, costs, and concrete risks
| Design choice | What it buys | What it costs |
|---|---|---|
| Empty root + patch composition | An exportable, overridable, live-updatable tree without hard-coded products | Configuration is code and can affect any plugin lifecycle |
| Wholesale replacement by ID | Deterministic override results and simple deletion semantics | Upper configurations can drift when they fail to restate newly added fields |
| Service-driven activation | Declaration order decouples from dependency order | YAML alone does not reveal the actual activation topology |
| Two-anchor resolution | Official installation and user plugins can compose into one Profile | Peer identity, symlinks, and package-manager behavior become runtime correctness concerns |
| Transactional boot/HMR | No half-valid tree; failures retain the last known-good state | Loader and effect disposal must be exceptionally reliable, and diagnostics become deeper |
| Web per-session presets | Capability and trust configuration can be isolated per session | The host/session ownership boundary is complex and every new plugin requires another audit |
DeepSeek Harness unifies deployment, product surfaces, Agent capabilities, and user extension under one patchable plugin-tree language. That unity is powerful: configuration dump, boot, hot reload, and per-session presets share underlying semantics. It does not remove complexity; it concentrates complexity in stable entry IDs, scope ownership, service dependencies, module identity, and transactional lifecycle. The design stays maintainable only when runtime invariants, composition tests, and observable effective configuration accompany it.
Chapter verification checklist
- Traced CLI profile dispatch through
runProfile(). - Verified the actual order of bundle, profile, home, CLI, and launcher overlays.
- Verified wholesale config replacement and service-driven rather than row-driven activation.
- Verified installation and Profile anchors plus the breadth-first module fallback.
- Traced boot commit points, failure cleanup, signal handling, and exit races.
- Used failure/recovery tests to verify last-known-good HMR semantics.
- Audited base, headless, Web, and the host/Agent-plane ownership boundary separately.
My Learning Notes
Autosaved only in this browser. Nothing is uploaded or committed. Export Markdown whenever you want to keep a copy.