No privileged core: the five-layer view
DeepSeek Harness has no fixed core. At startup, the runtime is assembled from layers of YAML configuration into a tree of plugins — the model adapter, the tool registry, the session log, and the agent loop itself are all nodes in that tree, and every one of them can be replaced from configuration. The architecture docs state it directly: “there is no privileged core to patch.”
Three entry points — the CLI, the Web SPA, and the Python SDK — share one runtime. Every capability the runtime exposes eventually converges on a layer of standard interfaces the project calls capability seams; only behind those seams do the real model APIs, file system, and operating system appear.
An easily missed fact: Cordis is not an npm dependency — its source is vendored wholesale into the repo. The vendor/ directory copies nine upstream packages (cordis, loader, hmr, schemastery, …) under the @deepseek-ai scope, with a CI gate that prevents registry copies from sneaking back in. The stated rationale: a framework layer that is “auditable, patchable, pinned.” Eighteen local modifications are already on record, including a deadlock fix.
Boot: a plugin tree assembled from patches
One detail captures the design philosophy: the root config file of a profile contains an empty entry list — literally [] — and is rewritten on every boot. The entire plugin tree is produced by applying patch layers in order. What your dsh is is fully determined by which patches were applied, and in what order.
A bundle is an ordinary npm package that declares a patch file in its package.json ("dsh": {"bundle": {"patch": "./cordis.patch.yml"}}). A single patch in dsh-base assembles roughly 80 plugins — model, session, agent, tools, credential management — while the web-app and headless bundles only apply incremental overrides on top. Config values may also be !!js expressions, evaluated at mount time:
# packages/bundle/headless/cordis.patch.yml (verbatim)
- id: session-query-sqlite
config:
path: ':memory:' # matched by id → whole-entry replacement
- insert:
- id: headless-runner
name: '@deepseek-ai/dsh-headless'
inject: [headlessStartup]
config:
task: !!js ctx.headlessStartup.taskThe Cordis kernel: fully reversible side effects
The accompanying 88-page paper, A Programming Paradigm for Spatiotemporal Composability, reduces to one implementation-level constraint: every resource a plugin registers while mounting — event listeners, tools, timers — must simultaneously record a cleanup function. Unloading runs those functions in reverse, restoring the runtime to its pre-mount state. Hot replacement without a process restart rests entirely on this mechanism.
The lifecycle of a turn
Two definitions: a turn is one full cycle of processing queued input; a step is one model call plus all the tool executions it requests. A turn contains one or more steps — tool results and injected context drive the next step until the agent stops naturally. The diagram below shows the main event sequence; every event name is verified against agent.ts.
The tool pipeline: log first, authorize second
The most engineering-dense part of the codebase. Three design decisions stand out: the call is written to the log before anything executes; any anomaly in the approval path is treated as a rejection (fail-closed) — there is no default-allow; and results are committed strictly in the order the model requested them.
The session log as the single source of truth
The core invariant: anything that enters a model request must be reconstructable from the session log. Three mechanisms enforce it. Structurally, model requests can only be derived from the log — no second code path exists. Every event is validated before append. And at runtime, an assertion plugin compares each outgoing request byte-for-byte against a fresh reconstruction from the log, aborting loudly on mismatch.
Context management is two-tiered: at every step boundary the runtime estimates token usage and compacts proactively past a threshold — pruning verbose tool output first, then summarizing older conversation with the session’s own prefix (deliberately preserved so KV-cache hits survive). If the provider actually returns a context-overflow error, compaction runs regardless of thresholds and the same step retries. Oversized tool output is handled separately (“spill”): the full text goes to disk, and the model sees only a head/tail preview plus a retrieval handle.
Capability seams: swap a provider, move the world
Every capability — files, processes, models — is split into three roles: a package that defines the interface, a package that implements it, and consumers that depend only on the interface. The practical payoff: moving the entire execution environment into a cloud sandbox takes two provider swaps in configuration.
The main seams (defaults per the dsh-base bundle)
| Seam (ctx key) | Default provider | Swappable to | Notes |
|---|---|---|---|
ctx.llm | llm-deepseek (deepseek-official route) | llm-pi-ai (multi-provider) / llm-replay | pi-ai ships mounted but dormant; a settings block activates it. Both adapters can coexist. |
ctx.fs | fs-sandbox (extends fs-local) | bare fs-local / fs-e2b | Its own comment: containment, not a security boundary. |
ctx.subprocess | subprocess-local (node-pty) | subprocess-e2b | The PTY primitive lives in this seam, not in the terminal packages. |
ctx.sandbox | sandbox-local | custom runner command | Linux: bwrap first, Landlock fallback (a home-grown static C binary); macOS Seatbelt; Windows restricted token (write-only enforcement). Unavailable sandbox = refuse to run, never a bare exec. |
ctx.subagents | spawn + fork (in-process) | acp / codex / claude-code / dsh-sdk | The claude-code provider resolves the claude binary from PATH and drives it via the official agent SDK; codex spawns codex app-server --stdio. |
ctx.codeRuntime | worker-thread (not in base) | other language backends | Where Code-mode programs run: an in-process worker executes the model’s TypeScript with types stripped; every tool call bridges back and re-enters the full pipeline. |
ctx.web | web-search-deepseek | exa / perplexity / http-fetch | web_fetch ships disabled; the comment cites SSRF risk. |
ctx.approval | user-approval | acp auto-answerer | Policies are only ask / never — and never means reject-all, not allow-all. |
The four presets — and the “Creator” that doesn’t exist
An agent preset selects the capability set for a single session; different sessions in one process can run different presets. Here the source diverges from the marketing: the official page and much of the press describe a fourth preset called “Creator” — but no such preset exists in the rc.5 source. The config directory contains exactly four: standard, code, cordis, minimal.
| Capability | standard | code | cordis | minimal |
|---|---|---|---|---|
| bash / pwsh / fs / search | ✓ | ✓ | ✓ | persistent bash + editor only |
| plan / todo / goal / skills | ✓ | ✓ | ✓ | ✗ |
| subagents / jobs / web search | ✓ | ✓ | ✓ | ✗ |
| compaction | ✓ | ✓ | ✓ | ✗ |
| Code mode (tools only via run_code) | ✗ | ✓ the sole diff vs standard is one config line | ✗ | ✗ |
| tool-cordis (model edits the live composition) | ✗ | ✗ | ✓ | ✗ |
A separate system, permission presets, is orthogonal to agent presets. It bundles two independent knobs into one user-facing option: sandbox level (read-only → workspace-write → full access) and approval policy (ask / never). Only two presets ship by default — workspace-write + ask, and full access + never — and every switch is recorded in the session log, so it replays.
The web trust model: origin checks, not authentication
The web deployment is two halves: a local host service (Node) and a React SPA. Commands go up as HTTP POST; real-time frames come down over two WebSocket channels. The essential point: there is no authentication — only an origin fence against DNS rebinding and cross-site requests, and the source comments state explicitly that the fence is not an auth layer.
What holds up, and what to watch
▲ Architecturally solid
- Log consistency is enforced, not aspirational: byte-level request verification, complete records for denied calls, synthetic results on abort — replayability gets unusual investment.
- The seam abstraction is field-tested: wholesale E2B migration, Claude Code and Codex mounted as subagents, twin model adapters coexisting — all evidence of stable interfaces over swappable implementations.
- Inference-cost awareness runs deep: forks and compaction both deliberately preserve cache-hittable request prefixes, and cache-hit metrics surface throughout.
- Security defaults are strict: no sandbox means no execution, approval anomalies mean rejection, credentials are write-only with 0600 file modes.
▼ Worth watching
- No authentication, only a fence: any local process sits inside the trust boundary (the root cause of disclosure #853); remote access is simply disabled until a real auth layer lands.
- Hot reload is actually off in both shipped deployments (the reload lifecycle is marked untested) — hot-swapping is an architectural capability, not yet a product reality.
- Docs have started drifting from code: the advertised “Creator” preset does not exist, and a documented
lsp-localpackage doesn’t either (it’slsp-stdio). - Preview-stage rough edges are visible: two RPC generations coexist, the Windows sandbox restricts writes only, and web fetch is switched off entirely over SSRF concerns.
In one sentence: this is not an agent with a plugin system — it is a plugin system that happens to be assembled into an agent. The bet is not benchmark scores; it is making the agent runtime the kind of infrastructure a web framework is: self-hosted, auditable, and rearrangeable at will. Vendoring the framework, composing from an empty config, and byte-checking the log are all down payments on that bet.
Methodology & sources
This analysis is based on a full read of the deepseek-ai/deepseek-harness source at 0.1.0-rc.5 (git 47f9438, snapshot 2026-08-14), conducted as four parallel passes: kernel & composition, event flow, capability seams, and the product layer. Key files: docs/architecture.md, vendor/README.md, apps/cli/src/profile-boot.ts, packages/boot/app-boot, vendor/cordis/src/fiber.ts, packages/core/agent-loop/src/agent.ts, packages/core/tools/src/index.ts, packages/core/session, packages/bundle/base/cordis.patch.yml, packages/client/connection/src/api-request-trust.ts. The project is iterating fast — expect details to change between rc releases.
New to DeepSeek Harness? Start with our complete guide — installation, troubleshooting, pricing, and how it compares to Claude Code and Codex.
Changelog: 2026-08-14 — first published, verified against dsh 0.1.0-rc.5.