4.2 KiB
@deepseek-ai/dsh-sandbox-local
Local implementation of the @deepseek-ai/dsh-sandbox seam: wraps a caller's argv in a platform confinement runner. Selection is BY PLATFORM, resolved once and cached: each platform names its runner chain, a chain of one is selected directly (probing arbitrates between candidates — a sole candidate leaves nothing to arbitrate), and a chain of several is probed functionally in preference order. Linux: bwrap when its probe passes, else the landlock-run Landlock launcher (kernel confinement that needs no userns/mount privileges — see the sandbox RFC for the prebuilt-binary decision and profile-parity notes); darwin: sandbox-exec speaking a Seatbelt (SBPL) profile, unprobed. A platform with no chain means confine() FAILS CLOSED with the seam's structured SANDBOX_UNAVAILABLE error (win32 today: a reserved, deliberately empty chain awaiting an AppContainer-family runner); an unprobed runner that turns out unusable fails closed at EXECUTION instead — it refuses to run the command, and every wrap's runnerFailureSignatures let the consumer classify that as a sandbox failure rather than a task failure. Never a silent unconfined passthrough on any path.
Policy is per call; the provider stores only the mechanism and cached runner verdict. Each wrap reports enforcement completeness plus backend-specific denial and runner-failure signatures. runnerCommand is an operator assertion of a bwrap-shaped runner and skips probes, but missing or unexecutable commands still fail closed at execution. Because its mechanism is unknown, it carries both Linux denial dialects. probeTimeoutMs bounds functional probes. The sandbox RFC owns selection and failure semantics.
The Seatbelt profile is allow-default with (deny file-write*) plus write allow-lists, so exactly the mode's promised file effects are governed: read-only grants the /dev/null literal alone; workspace-write adds the workspace root, /tmp, and the per-user darwin temp dir (os.tmpdir() — the platform's real temp area for mkstemp-family tools), every root canonicalized because Seatbelt matches resolved paths (/tmp IS /private/tmp). Apple marks the sandbox-exec CLI deprecated but ships it on every macOS; the functional probe is what fails closed if that ever changes.
The Landlock launcher comes from the npm package family node-addon-landlock-run — an entry package (this package's one runtime dependency) plus per-platform binary packages selected by npm's os/cpu fields, built and released from its own repository. The entry package owns the launcher's CLI contract: launcherPath() resolution (a host with no platform package yields a never-existing path whose probe fails exactly like an unenforcing kernel), the functional probe(), and grantArgs() flag spelling — versioned together with the binary, so probe-report parsing can never drift against it. This provider keeps only the policy side: the mode → grants mapping (landlockProfileArgs) and the ladder. The consumer path is rehearsed by tests/packed-install.e2e.ts: pack THIS package's closure, install into a throwaway consumer with the launcher family coming from the registry, assert the installed binary executable (a stripped mode bit must not masquerade as a non-enforcing kernel), and confine through it under plain node.
Every rung has its keyless world-proof (tests/bwrap.e2e.ts, tests/landlock.e2e.ts, tests/seatbelt.e2e.ts), each self-skipping where its runner is absent; CI's sandbox-e2e matrix runs all of them against real kernels (bwrap plus one Landlock leg per architecture on Linux, Seatbelt on macOS) and fails on a silent all-skip.
- id: sandbox
name: '@deepseek-ai/dsh-sandbox-local'
Consumers: @deepseek-ai/dsh-bash-sandbox; see examples/sandbox-acp-agent for the runnable composition.