description, kind
| description | kind |
|---|---|
| The sandbox-consuming Bash executor for deployments and maintainers choosing, configuring, or debugging confined command execution with denial and escalation facts. | package-reference |
@deepseek-ai/dsh-bash-sandbox
English | 中文
Summary
dsh-bash-sandbox is the sandbox-consuming Bash executor: every command runs as a fresh bash -c process confined through the ctx.sandbox capability instead of with the harness process's full file authority. Each settled result carries the mode the command ran under, whether the sandbox denied a file operation, and how completely the selected runner enforced the requested mode. When no runner can enforce a confined mode, the call fails closed with a structured SANDBOX_UNAVAILABLE error rather than running unconfined. It is the confining sibling of dsh-bash-local — sharing its process mechanics — and the tool layer's escalation fields appear only while it is mounted.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Mount this executor instead of dsh-bash-local when commands must not run with the harness process's full file authority. It registers as ctx.shell and requires a ctx.sandbox provider plus ctx.sandboxPolicy; the model-facing bash tool works over it unchanged and advertises the sandbox_permissions/justification escalation fields.
When to choose it
Choose it when a deployment needs file-level confinement for Bash commands: the configured policy decides the default mode and workspace root, and each session can run under a different mode per call through the tool's escalation flow. The modes govern file effects only — network stays unrestricted and process visibility is backend-specific. For unconfined execution, or when no sandbox backend is available on the platform, mount dsh-bash-local instead.
Modes and file effects
| Mode | File effects |
|---|---|
read-only (default) |
No writes anywhere; of /dev, only the /dev/null node is writable, so >/dev/null keeps working |
workspace-write |
Writes only under the policy's workspace root plus /tmp (ephemeral under bwrap, the host /tmp under Landlock, /private/tmp plus the per-user temp dir under Seatbelt) |
danger-full-access |
No confinement; the provider is never consulted, and results carry sandbox: { mode, denied: false } |
Minimal configuration
The executor takes no sandbox configuration of its own: the default mode and workspace root come from ctx.sandboxPolicy, and the runner choice belongs to the ctx.sandbox provider. Its own config is the local executor's knobs verbatim; the generated configuration catalog is the exhaustive source.
- id: sandbox
name: '@deepseek-ai/dsh-sandbox-local'
- id: sandbox-policy
name: '@deepseek-ai/dsh-sandbox-policy'
config:
mode: read-only
workspaceRoot: !!js process.cwd() # fallback for calls without a session cwd
- id: bash
name: '@deepseek-ai/dsh-bash-sandbox'
Denials are result facts
A denied command is reported, not retried silently: the result carries sandbox: { mode, denied: true } and the model-facing tool appends the denial marker. When escalation is available, the model may retry the exact command once with the narrowest wider mode and a one-sentence justification; the approval prompt asks the user, and nothing executes before approval. This executor never negotiates permissions itself — the tool layer drives the override.
Failures and recovery
If no runner can enforce a confined mode, the foreground call fails with SANDBOX_UNAVAILABLE and a background process records a runner-failure fact — never a silent unconfined run. A runner-attributable spawn failure carries the original spawn error as detail; other spawn rejections keep the local executor's ordinary command-start semantics.
Understand the implementation
Implementation internals — click to expand
This section explains the design of the executor and points at the code that realizes it; the observable behavior is fully covered in Use this package.
Design concept
The executor is the sandboxing Service Provider for the ctx.shell seam: it inherits dsh-bash-local's process mechanics and re-wraps each command's exact ['bash', '-c', command] argv through ctx.sandbox.confine(), spawning the returned argv directly. Which platform runner confines the command — and whether one is usable at all — is the provider's concern; this package owns the bash side only: the selected mode, enforcement completeness, and denial classification on results.
Source map
| File | Role |
|---|---|
src/index.ts |
Plugin entry: SandboxBashExecutor, per-process fact retention, run/start wrapping |
src/helpers.ts |
Denial, runner-failure, and runner-spawn-failure classification |
src/invariant.ts |
Invariant companion (no runtime invariant; classification is observable in results) |
tests/ |
Exercised behavior across the bwrap, Landlock, and Seatbelt runners |
Main flow
For a confined mode, resolve() stamps the per-call policy (the session's mode override, or the deployment fallback); run and start wrap the bash argv through the provider and hand the confined argv to the inherited subprocess path. At settlement the executor classifies the outcome: a runner failure outranks a denial because the command never ran, a failed run whose stderr carries the backend's denial dialect is reported denied: true, and every confined run carries its mode and enforcement facts. danger-full-access bypasses the provider entirely and stamps denied: false.
Invariants
- Fail closed — a confined mode with no usable runner throws
SANDBOX_UNAVAILABLE; unconfined passthrough never happens for a confined policy. - Deny-only at the seam — this executor never grants permission; the approval flow lives in the tool layer.
- Per-process facts — confinement facts are retained per handle until settlement, because a provider may vary enforcement between overlapping calls.
- File effects only — the mode vocabulary claims only file effects.
Further Exploration
Read these pages when the executor contract is not enough. They move from the seam to the sandbox capability this executor consumes.
- shell seam — the executor contract this provider implements, including the request/spec split.
- bash-local — the process mechanics this executor inherits.
- sandbox seam — the confinement capability, its modes, and its fail-closed contract.
- sandbox-policy — the per-session mode and workspace root this executor honors.
- sandbox-local — the shipped runner backends: bwrap, Landlock, and Seatbelt.
- tool-bash — the model-facing
bashtool and its escalation surface. - Sandbox Agent Note — the sandbox design, escalation, and switching contract.
Model Experience
Bash tool schema, indirectly
What the model sees
The generated dsh-tool-bash schemas are the baseline. By advertising a confining sandboxMode, this backend augments bash with sandbox_permissions (enum workspace-write | danger-full-access) and justification. The policy owner separately contributes the current capability-neutral sandbox:policy context.
Token effect
Small fixed schema increment on requests where bash is visible, plus the current-policy clause owned by dsh-sandbox-policy.
KV Cache effect
A standing-policy change appends a complete owner-rendered context snapshot after retained history, preserving the existing system/history prefix byte-for-byte. Changing executor capabilities alters the bash schema.
Bash tool result, indirectly
What the model sees
After ordinary bounded output, a denied call appends exactly [sandbox: file access denied under <mode> mode]. When escalation is available it next appends [sandbox: escalation available — retry this exact command once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]. A settled background runner failure instead appends [sandbox: the sandbox runner itself failed under <mode> mode — the command did not run; this is a sandbox problem, not a command failure].
Token effect
Zero additional tokens on an unremarkable allowed run beyond ordinary output. Denial or failure adds the quoted conditional marker, retained until compaction.
KV Cache effect
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Bash tool error, indirectly
What the model sees
If no runner can enforce a confined mode, the foreground call propagates the SANDBOX_UNAVAILABLE error from the sandbox seam. A runner-attributable spawn failure supplies the original spawn error as detail; a rejection without ENOENT/EACCES path or syscall evidence that names argv[0] remains an ordinary command-start error. A settled runner failure supplies the matched fatal stderr line and preserves the original stderr collection; the appended Runner failure: <detail> is the authoritative diagnosis over the generic SANDBOX_UNAVAILABLE prefix.
Token effect
Conditional error text is visible for that call and retained in history until compaction.
KV Cache effect
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Known Limitations and Deferred Work
These limits define when this executor is not a general security boundary. They are current package constraints, not a roadmap.
- Confinement covers file effects only — network restriction and a uniform process-visibility guarantee are absent, so the modes are not a general-purpose security sandbox.
- Denials are inferred from failed-command stderr — backend signatures make the inference portable, but a matching application error can be classified as a denial and a denial omitted from the retained tail can be missed.
- An asynchronously observed background runner failure has no immediate error channel — it is recorded on the settled process and surfaces when the caller reads the generic task with
job_output; a synchronous subprocess throw that names the runner path instead failsstart()immediately. danger-full-accessdeliberately bypassesctx.sandbox— it is an explicit unconfined mode, not a wider sandbox profile.
Dev Note
Working context for maintainers — click to expand
None.