dsh-plugin-approval-alert
doncelee229-cmyk
DeepSeek Harness 审批/选择方案系统级通知提醒,显示工作区名、点击跳转、多语言。Approval & decision alerts with native notifications for DeepSeek Harnes…
tancheng33/dsh-code-runtime-container
Container-isolated backend for the DeepSeek Harness code-execution seam: Code Mode programs run in a fresh container with no network, a read-only rootfs, and kernel-enforced memory, CPU, and pid ceilings
PROJECT TOPICS
PROJECT README
English | 中文
A container-isolated backend for the DeepSeek Harness code-execution seam (ctx.codeRuntime). Code Mode programs run in a fresh container with no network, a read-only root filesystem, every capability dropped, and kernel-enforced memory, CPU, and pid ceilings.
The seam declares three well-known isolation substrates and ships one. From @deepseek-ai/dsh-code-runtime's own README:
Only the worker-thread backend ships —
'process'/'container'are declared well-knownisolationvalues with no implementation; a hard security boundary awaits a container backend.
And the shipped backend is equally explicit about its posture:
Containment, not a security boundary: trust posture is bash-equivalent by design.
That is a reasonable default — Code Mode programs are model-written code, and so is everything bash runs. But it means a run_code program executes inside the agent's own process, with the agent's network access, the agent's filesystem, and whatever the agent's environment holds. This backend is for deployments where that is not acceptable.
| worker-thread (shipped) | this backend | |
|---|---|---|
| Substrate | a Worker in the agent process |
a fresh container per run |
| Network | full agent network access | --network=none |
| Filesystem | the whole host, as the agent user | read-only rootfs, no host mount |
| Privileges | the agent's | --cap-drop=ALL, no-new-privileges, nobody |
| Runaway CPU | measured busy-time budget | kernel CPU quota + wall clock |
| Runaway memory | V8 heap cap | cgroup memory limit → OOM kill |
| Fork bomb | — | --pids-limit |
| Cold start | ~milliseconds | ~200 ms |
dsh plugin --profile <name> add dsh-code-runtime-container
Requires a container engine on the host and the image available locally:
docker pull node:22-alpine
The bundle patch disables the worker-thread code-runtime row as it inserts this one: ctx.codeRuntime accepts exactly one implementation per context.
No image build and no bind mount are needed — the in-container runner is passed to node -e on the command line, so the stock upstream image works as-is. podman and nerdctl work through dockerPath.
Every default is the restrictive one; a backend whose point is isolation must not be safe only after configuration.
| Key | Default | Meaning |
|---|---|---|
dockerPath |
docker |
Container CLI. Anything argv-compatible with docker run. |
image |
node:22-alpine |
Needs a node on PATH and nothing else. |
network |
none |
Docker network mode. The main reason to use this backend. |
memory |
512m |
Memory ceiling; an overrun is an OOM kill reported as worker-exit. |
cpus |
1 |
Kernel-enforced CPU quota. |
pidsLimit |
128 |
Process ceiling: a fork bomb hits it, not the host. |
user |
65534:65534 |
nobody:nogroup. Empty uses the image default. |
readOnlyRootfs |
true |
Read-only root, with a tmpfs at /tmp. |
tmpfsMb |
64 |
Size of that tmpfs. |
workspacePath |
'' |
Absolute host path to mount at /workspace. Empty mounts nothing. |
workspaceReadOnly |
true |
Mount the workspace read-only. |
extraArgs |
[] |
Extra docker run flags. An escape hatch that can weaken every default above. |
maxWallMs |
120000 |
Wall-clock ceiling for one run, including container start. |
maxOutputBytes |
4194304 |
Combined cap on logs plus completion value. |
Programs that must read project files need a mount:
- id: code-runtime-container
config:
workspacePath: /Users/me/projects/app
workspaceReadOnly: true
# …restate the rest; a patch replaces a row's whole config
The seam is explicit that isolation is "a label for deployments and diagnostics, not a security claim". So here is the actual claim, stated narrowly.
What a program cannot do, verified by tests against a real container:
fetch fails; --network=none).EROFS)./workspace does not exist unless you mount it.uid is 65534).while(true){} is killed, and the container is killed by name — not merely the docker run client.argv or env, both of which the program can read. A program writing {"t":"done","value":"FORGED"} to stdout has that line accounted as output, not control.JSON.stringify and process.stdout.write are captured before the program runs.What this does NOT protect against:
dockerPath pointing at a Kata/Firecracker-compatible CLI) or an isolated host.workspacePath is a real write path into your project. That is the point of the option, and it is the one setting that meaningfully widens the boundary.extraArgs opens. It is passed to docker run verbatim; --network=host there undoes the headline property.dsh-egress-guard for the tool-call side.The seam contract is honored as written:
{ logs, error: { kind, message } }. run() rejects only for contract misuse: a disposed runtime, or a binding namespace that violates the portable-identifier rules.PORTABLE_RESERVED_WORDS, RESERVED_BINDING_GLOBALS, RESERVED_ERROR_MEMBERS, DUNDER_MEMBER), not restated here — so lambda is refused on this TypeScript backend exactly as it would be on a Python one, and a widening of the union reaches this backend with a dependency bump.__proto__ or constructor is an ordinary member.errorClass is materialized in the program, so e instanceof ToolCallError works and the failed member name lands on the declared property.await and return work. The program is wrapped, stripped, and sliced back out by byte offset (mode: 'strip' preserves positions), so a bare return is not a syntax error.enum and namespaces are a program failure, not a silent transform.computeMs has no equivalent. The worker backend meters measured event-loop busy time to stop a hot loop hiding behind a pending dispatch. Across a container boundary that measurement is not available, so CPU is bounded by the kernel (--cpus) and elapsed time by maxWallMs. This is a genuine behavioral difference: a program that sleeps for a long time consumes wall budget here, where the worker backend would only charge it busy time.isolation reports 'container', which the seam treats as informational.63 tests, 26 of which run against a real container engine — including every isolation claim above, both budget kinds, cancellation, substrate death, and the two hostile-program cases (a forged protocol frame and a replaced JSON.stringify).
npm test # unit tests only
docker pull node:22-alpine
DSH_CONTAINER_TEST=1 npm test # + live container suite
language is 'typescript'. The seam declares 'python' as the other well-known value and dsh-tools already ships a Python SDK renderer, but no Python backend exists yet — including this one.run() is one-shot: logs arrive on the resolved result. A killed program still shows what it printed before it died.worker-exit failure naming the engine's output; pull it ahead of time.maxOutputBytes rather than a stream backpressure path.MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。