Everything Is a Plugin: What DeepSeek Harness Actually Ships
DeepSeek Harness is not another chat skin. It is a Cordis plugin tree where the model adapter, tools, sessions, sandbox policy, and the Web UI itself are replaceable rows.
When people first open the DeepSeek Harness docs, the slogan hits hard: Everything is a Plugin. That line is easy to dismiss as marketing until you look at what actually boots when you run npx @deepseek-ai/dsh web. There is no privileged “core app” you are supposed to fork. There is a composition of Cordis plugins, ordered by profile and patch files, and almost every capability you notice in the UI is one of those plugins.
This note is a map for plugin authors and for people who only want to install community packages without getting lost. It is developer notes from using the public Harness docs and repo. It is not an official guide, and it is not affiliated with DeepSeek AI.
Why call it a harness instead of a chat UI
A chat UI decides how messages look. A harness decides how an agent is allowed to think, call tools, persist sessions, ask for approval, and reload configuration. DeepSeek Harness sits in the second category. The Web UI is real and useful, but it is also just another layer—shipped as dsh-web-app—stacked on top of a shared base.
That design choice matters if you are used to agent frameworks where “plugins” mean a thin tool registry glued onto a fixed runtime. In Harness, the runtime pieces are plugins too. Swap the model adapter. Replace the tool registry. Change approval policy. Mount a different session store. You are editing the same kind of object the product itself is built from.
The practical consequence is humility about “the app.” When something misbehaves, the debugging question is rarely “which fork of the monolith do I patch?” It is “which Cordis row owns this behavior, and can I replace or unload it?” That question is what the rest of this series keeps returning to.
Cordis in two axes (very short)
Harness vendors Cordis, the framework described in the spatiotemporal composability paper (arXiv:2608.25512). You do not need the full paper to write a useful plugin, but two ideas keep showing up in day-to-day work:
- Temporal. Registrations are reversible effects. When a plugin unloads, listeners, tools, and prompt sections it installed should unwind cleanly. Hot reload and safe teardown depend on that discipline. If your plugin mutates a global and never registers an undo, you are fighting the framework.
- Spatial. Plugins declare what they need with
inject, then respond when the graph around them changes. Load order becomes “wait for these services” instead of a brittle manual boot script. Two plugins can be correct in isolation and still fail together if a required service name never appears onctx.
A Cordis plugin is usually a small object: a name, an optional inject list, and an apply(ctx) function that mounts services and effects into a shared context. Services claim stable keys such as ctx.tools or ctx.sessions. Typed events let plugins observe, wrap, or fan out work without importing each other’s concrete classes. That is the unit DeepSeek Harness composes everywhere.
The replaceable checklist
If you dump a web profile config, you will see rows that look like product features, not “extensions.” Common categories include:
- Model adapters and credential plumbing
- Tool registration and schemas the model can call
- Session and persistence services
- Sandbox and approval policy
- Settings, telemetry, and the Plugin Manager itself
- The browser application layer for the Web UI
Official architecture docs describe dsh-base as the shared first layer for most shipped profiles, with dsh-web-app, dsh-headless, dsh-sdk-app, and dsh-acp-app adding the surface you actually launch. The deliberate exception is sdk-minimal, which owns an explicit tree and does not apply dsh-base.
That layering is why “I only wanted a different tool” sometimes forces you to understand profiles anyway. Tools live on top of services that live inside a profile composition. Jumping straight to a random community package without knowing which profile you patched is how people lose an evening.
Profiles and bundles are compositions, not kernels
A profile is a named composition stored in the Harness home. It lists bundles, out-of-tree installs, and the user’s cordis.patch.yml. Shipped templates include web, headless, sdk, sdk-minimal, and acp.
A bundle is a distribution format: Cordis config rows plus the code those rows mount. Layers apply to an empty entry list—profile bundles, profile patch, home patch, then --patch overlays. A patch can replace a row by id or insert new ones. That is why installing a community plugin feels like “adding a line to the tree” rather than patching a private monorepo.
YAML also controls hot-reload defaults: base tends to enable config-oriented HMR helpers; headless, SDK, and ACP profiles often disable or omit them. Profile patches can override those defaults. You do not need to memorize every flag on day one, but you should expect “I edited a file and nothing happened” to be a profile/HMR question before it is a Cordis bug.
To see what your machine actually boots:
dsh --profile web --dump-config
Any row that printout shows is, in principle, replaceable by a patch of your own. Keep a few dumps around. Diffing yesterday’s tree against today’s is the fastest way to notice an install that claimed success while writing nothing useful.
What this means if you write plugins
You are not forking Harness to add a feature. You are mounting a plugin beside the others and letting Cordis own lifecycle. The practical habits that follow:
- Prefer reversible effects over global side effects.
- Declare dependencies with
injectinstead of racing boot order. - Ship configuration as patchable rows so users can override you.
- Document the services you publish and the permissions you need.
- Expect breaking changes—Harness is still in developer preview.
If you are coming from “agent framework as library,” the mindset shift is the whole product. Libraries ask you to call them. Harness asks you to join a tree that already knows how to unload you.
Next
The map only helps if you can run the tree and install one package without drama. The next note walks through a first afternoon with dsh web and a bundle plugin: isolated home directory, install command, dump-config check, and the restart pitfall that catches almost everyone once.
Further reading: the deepseek-harness repo, the product page at deepseek.com/en/harness, the Cordis primer in the repository docs, and the architecture notes under docs/architecture.md.