dshkit

The DeepSeek Harness sandbox: modes, backends and what it does not protect

How dsh confines process execution — the three sandbox modes, per-session overrides as log events, the bwrap/Landlock/Seatbelt/Windows-ACL backends, fail-closed behaviour, and the partial-enforcement limits the project documents honestly.

Updated 2026-08-145 min
Short answer

Sandbox mode is read-only (the fail-safe default), workspace-write, or danger-full-access, resolved per call by ctx.sandboxPolicy. A per-session override is one append-only sandbox/mode event, so it survives restart by replay. Local backends are bwrap or Landlock on Linux, Seatbelt on macOS, and an ACL restricted token on Windows; unsupported platforms fail closed with SANDBOX_UNAVAILABLE rather than running unconfined.

Most agent tools describe their sandbox in marketing terms. DeepSeek Harness documents where its sandbox is partial, which is a better signal about the engineering than any claim of complete isolation would be.

Three packages

PackageRoleKey
dsh-sandboxDefines the process-sandbox service and escalation vocabularyctx.sandbox
dsh-sandbox-localLocal platform confinement backendsregisters on ctx.sandbox
dsh-sandbox-policyResolves durable per-session policyctx.sandboxPolicy

The family covers same-world subprocesses. Isolated environments replace complete capability implementations rather than registering here — a distinction worth holding onto, because it means "sandbox" here is about confining local process execution, not about running your agent somewhere else.

The three modes

ModeEffect
read-onlyThe default. No file modification through the DSH file sandbox.
workspace-writeWrites permitted under the session workspace root.
danger-full-accessNo confinement.

read-only is the deployment default and the source calls it fail-safe — the correct default for a system that can execute shell commands in your repositories.

Why policy has one home

Filesystem tools, one-shot bash commands and terminal sessions may all enforce the same mode vocabulary in different combinations. If each resolved its own mode and workspace root, they could drift into a split world — one tool believing it is read-only while another writes.

So ctx.sandboxPolicy is the single owner. Every enforcing capability receives one resolved mode-and-root policy per call:

ctx.sandboxPolicy.resolve({ session?, mode? })

Precedence: an explicit approved mode outranks the session's last sandbox/mode event, which outranks defaultMode. The session's immutable cwd is canonicalized with filesystem semantics before becoming workspaceRoot, otherwise the configured fallback applies.

Config

KeyDefaultMeaning
moderead-onlyDeployment default mode, validated at load
workspaceRootprocess.cwd()Fallback write root for agentless calls or sessions with no cwd

A normal agent call uses its session header's immutable cwd instead of the fallback.

A mode switch is an event

setSandboxMode(session, mode)   // appends exactly one sandbox/mode event
effectiveSandboxMode(events)    // pure fold; last switch wins

This is the harness's event-sourcing discipline applied to permissions. The switch IS its event — nothing mutates the mode out of band. So:

  • an override survives restart by replay
  • two sessions never see each other's state
  • the exact policy input stays reconstructable, because the agent loop logs the assembled runtime context snapshot as a sourced user/message

The optional ./invariant companion rejects a forged durable sandbox/mode event whose value falls outside the closed vocabulary.

What the model is told

The sandbox:policy contribution appears in every agent session's runtime-context snapshot. It states the mode's capability-neutral file-effect contract — and deliberately does not enumerate mounted capabilities.

The read-only text is instructive:

Current DSH file policy: read-only. Any available operation enforced by the DSH file sandbox cannot modify files in the standing mode. Do not refuse a required modification from this policy alone: try an available tool normally and follow any denial and escalation guidance it returns.

That last sentence is careful prompt engineering. Told only "you are read-only", a model tends to pre-emptively refuse work it could actually do. The instruction pushes it to attempt and read the denial rather than reason from the policy label.

The backends

dsh-sandbox-local selects and caches one platform runner:

  • Linux — a working bwrap, then Landlock
  • macOS — Seatbelt
  • Windows — an ACL restricted-token runner

Multiple candidates are probed in order; a sole candidate is selected directly.

Each wrap reports enforcement completeness plus backend-specific denial signatures and runner-failure rules, so consumers can distinguish a broken sandbox from a command failure. Landlock requires exit 125 and a landlock-run: fatal line; bubblewrap and Seatbelt remain signature-only because neither public contract reserves a launcher-failure status.

The macOS Seatbelt profile is allow-default with (deny file-write*) plus write allow-lists. read-only grants the /dev/null literal alone; workspace-write adds the workspace root, /tmp and the per-user darwin temp dir — every root canonicalized, because Seatbelt matches resolved paths (/tmp is /private/tmp).

The limits the project states about itself

This is the part worth reading before you rely on any of it.

Windows ACL enforcement is partial. The restricted token must retain Everyone for process initialization, so external objects granting Everyone write access remain writable; NTFS hard links also alias one file object across workspace and external paths. The provider reports enforcement: 'partial' rather than overstating the boundary.

Landlock may be partial. Older supported kernel ABIs confine only the access classes they expose, again reported as partial.

Seatbelt depends on deprecated sandbox-exec. Apple marks the CLI deprecated but ships it on every macOS; the functional probe is what fails closed if that changes.

Runner selection is cached for the provider lifetime. Installing, removing or repairing a runner requires reloading the plugin before selection changes.

runnerCommand is an operator assertion. A configured custom runner skips functional probes and is assumed to implement the bwrap-compatible profile honestly. And a sharp detail: if that runner is itself a Bash script, its interpreter startup runs before the script applies confinement.

What to actually do with this

In CI, set the mode deliberately. An unattended agent with repository write access is a deployment decision. Sandbox and approval policy are patchable rows from dsh-base, which cuts both ways — see patching plugin config and put the patch that encodes your choice in version control where a reviewer sees it.

Do not treat workspace-write as containment on Windows. The partial-enforcement note is explicit, and hard-link aliasing is not theoretical.

Do not rely on the workflow worker thread as a security layer — it is not one, and the sandbox seam is a different mechanism entirely.

Frequently asked

What is the default sandbox mode?

read-only, chosen as the fail-safe default. workspace-write and danger-full-access are the other two values, validated at load.

What happens on an unsupported platform?

It fails closed with SANDBOX_UNAVAILABLE. Execution never silently falls through unconfined — that is the property that makes the seam trustworthy.

Is the Windows sandbox complete?

No, and the project says so. The restricted token must retain Everyone for process initialization, so external objects granting Everyone write access remain writable, and NTFS hard links alias one file object across paths. The provider reports enforcement: 'partial' rather than overstating it.

How is a per-session mode change stored?

As exactly one append-only sandbox/mode event on that session. The switch IS its event — nothing mutates the mode out of band, and the effective value is explicit grant, then the fold of events, then the deployment default.

Keep reading