Architecture · 05
Profiles, Bundles, and Patches: How the Harness Is Assembled
Inspect the complete composition, install profile-owned extensions, apply whole-row patches safely, and prove precedence before boot.
- Reading time
- 17 minutes
- Sources verified
Build the right composition model before editing YAML
DeepSeek Harness begins with an empty Cordis row list and builds a runnable product by applying ordered layers. A Profile is the named, user-owned composition selected by the launcher. It records an ordered Bundle list, owns a profile directory for out-of-tree dependencies, and carries its own cordis.patch.yml. A Bundle is a distribution unit whose package manifest points to a patch file. A patch is an overlay that replaces a row's complete config or inserts a new row.
These concepts solve different problems. A Profile answers ‘which product shape am I booting?’ A Bundle answers ‘which cooperating rows ship together?’ A patch answers ‘what does this deployment change?’ Conflating them produces brittle configurations: copying an entire shipped Bundle into a home patch makes upgrades opaque, while modifying a Bundle under node_modules loses user ownership and is overwritten by package updates.
empty row list
→ profile Bundle 1 (normally @deepseek-ai/dsh-base)
→ profile Bundle 2 (for example @deepseek-ai/dsh-web-app)
→ profile cordis.patch.yml
→ $DSH_HOME/cordis.patch.yml
→ each --patch overlay
= resolved Cordis rows- Use a Bundle to distribute a coherent reusable capability set.
- Use a profile to select Bundles and own extension dependencies.
- Use profile patches for changes specific to one product shape.
- Use the home patch for changes intentionally shared across profiles.
- Use --patch for an explicit invocation overlay whose provenance is recorded.
Understand what the shipped base, Web, and headless layers contribute
The base Bundle is the first layer of every shipped profile. It mounts common model adapters, tools, persistence, settings, credentials, sandbox and approval policy, and telemetry. The Web application Bundle adds the browser application and its host/client services. The headless Bundle adds a one-shot runner with no server. Neither Web nor headless replaces the base; each stacks a product surface on the shared capabilities.
The CLI auto-initializes Web and headless profiles from shipped templates on first use. A different profile name must be created through dsh plugin, which starts with the base Bundle when no shipped template exists. In-box Bundles resolve from the same dsh installation as the launcher. Out-of-tree Bundles resolve from the profile's pnpm-managed node_modules. This prevents an arbitrary profile dependency from shadowing the launcher's own base packages.
dsh --profile web --dump-default-config > /tmp/web-default.yml
dsh --profile headless --dump-default-config > /tmp/headless-default.yml
diff -u /tmp/web-default.yml /tmp/headless-default.ymlInspect the profile directory and manifest as deployment state
A profile directory lives under $DSH_HOME/profiles/<name>. Its package.json declares dsh.profile and the ordered Bundles. Its node_modules contains out-of-tree plugins managed by pnpm. Its cordis.patch.yml is the profile-owned overlay. Treat these files as deployment configuration: back them up, review them, and avoid editing generated pnpm links by hand.
{
"private": true,
"dependencies": {
"@example/dsh-audit": "1.2.3"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app"
]
}
}
}The exact generated manifest may contain additional package-manager fields, so inspect the initialized profile rather than replacing it with this illustration. Bundle order is meaningful. A Bundle package declares its own patch under dsh.bundle; after a successful plugin add or update, the CLI reconciles installed Bundle declarations into the profile stack. A dependency without a Bundle declaration remains a plain plugin dependency available to explicit rows.
dsh plugin --profile web why @deepseek-ai/dsh-web-app
dsh plugin --profile web list --depth 0
dsh --profile web --dump-config > /tmp/resolved.ymlUse default and resolved dumps as the configuration review surface
Use --dump-default-config to inspect the composition before user patches and --dump-config to inspect the fully resolved tree. Both modes avoid normal application boot, so they are suitable for review and CI. Archive both outputs with the profile dependency lock when a deployment is approved. A source Bundle file alone is not sufficient evidence because later layers may replace its rows.
dsh --profile web --dump-default-config > default.cordis.yml
dsh --profile web --dump-config > resolved.cordis.yml
diff -u default.cordis.yml resolved.cordis.ymlReview ids, plugin names, dependency order, and complete configs. Search explicitly for security and data-bearing capabilities. Confirm one intended provider for each exclusive seam and verify that consumers are not left pending. The dump should contain credential references, not literal secrets; still review local paths and endpoint metadata before sharing it.
grep -nE "credentials|session-persistence|permission|sandbox|telemetry|llm|tools" resolved.cordis.yml
! grep -nE "sk-[A-Za-z0-9]{16,}" resolved.cordis.ymlA successful review explains every diff hunk as an owned profile, home, or command overlay. An unexplained row means the deployment is not reproducible yet.
Patch with whole-row replacement semantics in mind
A patch targets a row by id. When it supplies config, that config replaces the row's previous config as a whole; nested values are not deep-merged. Copy the complete resolved config you intend to retain, then change the required field. This is the most important operational rule in Harness configuration because a concise patch can accidentally remove a timeout, provider model list, runtime expression, or security default.
# profile cordis.patch.yml
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
apiKeyEnv: DEEPSEEK_API_KEY
baseURL: https://api.deepseek.com
thinking: enabled
reasoningEffort: high
streamIdleTimeoutMs: 300000The example is intentionally complete enough to demonstrate preservation, but the official adapter README and current resolved row remain authoritative. Validate every field at the revision you deploy. To remove a change, restore the prior complete row or remove the overlay and compare dumps; do not append a second compensating patch whose interaction only the author understands.
Bad assumption: { streamIdleTimeoutMs: 600000 } deep-merges into old config.
Actual rule: that object becomes the row's complete config.
Safe workflow: copy resolved config → edit one field → dump → diff → boot → test.Preserve CLI-fed expressions when flags must override configuration
Application plugins parse their arguments and expose resolved values as services. Rows can evaluate config expressions against those services. The Web startup port is an example: a row may retain an expression that reads ctx.webStartup.port and falls back to 3080. A CLI flag wins only while the row still evaluates that service. Replacing the whole config with a literal removes the runtime read, so future --port values appear to be ignored.
# Conceptual expression-bearing row
- id: host-webserver
name: '@deepseek-ai/dsh-host-webserver'
config:
port: !!js ctx.webStartup.port ?? 3080Launcher flags must precede the first application argument, and flags after dsh web belong to the Web app. Help requests exit zero without activating rows that depend on the provider service; rejected arguments exit nonzero. Use current help rather than copying flags from another profile.
dsh --help
dsh web --help
dsh web --port 3081
# Unsupported today: dsh web --host 0.0.0.0Verification requires both configuration and behavior. Confirm the resolved row still contains the expression or its evaluated winner, launch with the flag, and connect to the printed loopback address. If the served port does not change, compare the last patch targeting the Web server row.
Install profile-owned plugins and Bundles through the CLI
Use dsh plugin --profile <name> to manage dependencies. The launcher initializes the profile when missing, then forwards add, remove, why, update, and other arguments to pnpm with the profile directory as cwd. Relative path specifications are anchored to the invoking directory first. Running add . from a plugin checkout therefore installs that checkout rather than resolving dot inside the profile directory.
cd /path/to/reviewed-plugin
dsh plugin --profile web add .
dsh plugin --profile web why @example/dsh-audit
dsh --profile web --dump-config > /tmp/after-install.ymlAfter a successful command, dependencies declaring dsh.bundle join the ordered Bundle stack. Plain plugin packages do not mount automatically; add a Cordis row to the profile patch. Removing a Bundle dependency also removes it from the stack during reconciliation. Review the package manifest before installation because Bundle membership grants it composition authority.
Git-hosted source dependencies may run prepare during installation. pnpm 10+ blocks unapproved dependency builds and prints an allowBuilds key. Add only the exact reviewed key to the profile's pnpm-workspace.yaml, rerun, and inspect generated output. A built tarball or local checkout may not require that allowance.
# profile pnpm-workspace.yaml — only after reviewing pnpm's exact failure
allowBuilds:
'@example/dsh-audit': trueUnderstand watched patches and transactional replacement
Every profile boot watches valid edits to the profile and home cordis.patch.yml. A valid change is reapplied transactionally: the affected old Fiber and its effects unwind, then replacement behavior commits. This is why plugins must own reversible effects. An invalid edit should not leave a half-mounted row, but it will produce a diagnostic that must be fixed before the desired configuration becomes active.
valid edit
→ parse and validate candidate rows
→ prepare replacement Fibers and dependencies
→ dispose superseded effects
→ commit new composition
invalid edit
→ report validation/load failure
→ do not treat requested state as activeA live patch edit re-evaluates expressions against services that remain up, but it cannot retroactively reset a served port after the server already bound it. Some changes therefore require a process restart even when the patch transaction itself is valid. Define restart requirements per plugin instead of promising universal hot reload.
dsh --profile web --dump-config > /tmp/before.yml
# Edit the profile patch, observe host diagnostics, then:
dsh --profile web --dump-config > /tmp/after.yml
diff -u /tmp/before.yml /tmp/after.ymlDebug precedence and startup failures without adding more overlays
When a value is wrong, locate the last layer that targets its row. Begin with default versus resolved output. Inspect the profile patch, then the home patch, then every --patch argument in invocation order. Do not add a fifth override to counteract an unknown third override. Remove or correct the owner closest to the intended scope.
diff -u default.cordis.yml resolved.cordis.yml
rg "id: llm-deepseek" "$DSH_HOME/profiles/web/cordis.patch.yml" "$DSH_HOME/cordis.patch.yml"
printf '%s\n' "the exact launch command and every --patch argument"Classify boot failures. A missing plugin name points to profile dependency resolution. A pending row points to an unavailable injected service. Schema rejection points to config. A flag rejected before activation points to command grammar. A server bind error points to port or host state. A provider request failure after boot belongs to credentials, endpoint, model, or protocol rather than Bundle precedence.
Failure decision
row absent → Bundle list or patch insertion
module absent → profile dependency / installation fallback
row pending → required service/provider visibility
config rejected → schema or whole-row omission
flag ignored → expression removed by replacement
boot succeeds, request fails → runtime provider path- Preserve the first specific diagnostic.
- Record the resolved row and owning overlay.
- Restore the last known complete config before testing another hypothesis.
- Never print credential values while debugging references.
Govern profiles as versioned deployment artifacts
A production profile consists of more than cordis.patch.yml. Pin the dsh installation, profile package dependencies, Bundle order, profile and home patches, invocation overlays, environment references, and expected resolved dump. Review changes as code. A profile that depends on an unpinned local checkout or an undocumented home patch cannot be reproduced by another operator.
Build a release test around the capabilities that composition controls: model selection, credential resolution, session persistence and resume, tool modes, workspace confinement, approval, sandbox, telemetry, Web trust, and bounded shutdown. Exercise both allowed and denied operations. Verify that a patch rollback removes the new row and disposes its resources rather than merely hiding its UI.
Profile release record
- dsh package or commit
- profile package.json and lockfile
- ordered Bundle list
- profile/home/CLI patch inventory
- redacted default and resolved dumps
- capability smoke and denial results
- restart and rollback procedureDeveloper preview brings compatibility-breaking changes. Upgrade in a clone of the profile, compare default and resolved trees, and test representative sessions before replacing the active installation. Do not let an automatic dependency update silently alter Bundle declarations or row schemas. Composability is an operational advantage only when layer ownership and version provenance remain explicit.
cp -R "$DSH_HOME/profiles/web" "$DSH_HOME/profiles/web-upgrade-review"
dsh --profile web-upgrade-review --dump-config > candidate.yml
diff -u approved.yml candidate.ymlOfficial sources
- Harness architecture ↗Supports: Profiles and Bundles, layer precedence, whole-row patch semantics, base/Web/headless composition
- CLI profile boot reference ↗Supports: Profile resolution, expressions and flags, plugin management, watched patches, shutdown
- CLI application README ↗Supports: Entry modes, profile directory, Bundle resolution, dump commands
- Application boot package ↗Supports: Profile initialization, Bundle and patch loading
- Base Bundle ↗Supports: Shared base capabilities and rows
- Headless Bundle ↗Supports: One-shot headless composition without server
- Cordis configuration tutorial ↗Supports: Plugin config contracts and validation

