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.

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

bash
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.yml

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

json
{
  "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.

bash
dsh plugin --profile web why @deepseek-ai/dsh-web-app
dsh plugin --profile web list --depth 0
dsh --profile web --dump-config > /tmp/resolved.yml

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

bash
dsh --profile web --dump-default-config > default.cordis.yml
dsh --profile web --dump-config > resolved.cordis.yml
diff -u default.cordis.yml resolved.cordis.yml

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

bash
grep -nE "credentials|session-persistence|permission|sandbox|telemetry|llm|tools" resolved.cordis.yml
! grep -nE "sk-[A-Za-z0-9]{16,}" resolved.cordis.yml

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

yaml
# 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: 300000

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

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

yaml
# Conceptual expression-bearing row
- id: host-webserver
  name: '@deepseek-ai/dsh-host-webserver'
  config:
    port: !!js ctx.webStartup.port ?? 3080

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

bash
dsh --help
dsh web --help
dsh web --port 3081
# Unsupported today: dsh web --host 0.0.0.0

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

bash
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.yml

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

yaml
# profile pnpm-workspace.yaml — only after reviewing pnpm's exact failure
allowBuilds:
  '@example/dsh-audit': true

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

text
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 active

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

bash
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.yml

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

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

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

text
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 procedure

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

bash
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.yml

Official sources