DSH / PLUGIN / E2B

dsh-subprocess-e2b

v0.1.0-rc.5deepseek-ai / deepseek-harness47f943859b

Included in DSHPluginsRuntime & platformBuilt-in sourceConfigurable
Runtime anatomy
HOSTCLIENTUITOOLDATAFLOW

Overview

dsh-subprocess-e2b

E2B implementation of the @deepseek-ai/dsh-subprocess seam. Load @deepseek-ai/dsh-e2b first, then this service in place of dsh-subprocess-local. Existing Bash, PTY, and LSP consumers then execute in the shared remote sandbox without E2B-specific capability packages.
BUILT-IN / ATOMIC
Already shipped with DSH — no separate install

This is an atomic module already shipped with Harness, not a standalone profile layer.

Capabilities

What it contributes

HostCordis loadableConfigurable
Client / UIHost only0 contributions
Model tools0None declared
Profile stateabsentIncluded in DSH

README / EN

Package documentation

@deepseek-ai/dsh-subprocess-e2b

English | 中文

E2B implementation of the @deepseek-ai/dsh-subprocess seam. Load @deepseek-ai/dsh-e2b first, then this service in place of dsh-subprocess-local. Existing Bash, PTY, and LSP consumers then execute in the shared remote sandbox without E2B-specific capability packages.

Configuration

Key Default Meaning
pollMs 20 Remote status/liveness poll cadence in milliseconds; each tick is one control-plane request, so a larger value trades exit-observation latency for fewer requests.

Behavior

  • Asynchronous remote start — the synchronous seam returns a handle immediately while Sandbox.commands.run(..., { background: true }) starts remotely. pid is -1 until the wrapper publishes and the adapter validates its process-group id; stdin and ordinary observation wait for that publication. An owned startup signal aborts environment and private-state preparation before allocation; once allocation begins, cancellation waits for a provisional SDK handle it can clean.
  • Execution-world coordinatescwd and private runtimeRoot come from the shared owner; executable lookup verifies absolute paths or resolves a bare name against the sandbox PATH plus explicit overrides, and rejects relative paths containing separators like every subprocess provider.
  • Linux process groups — a quoted wrapper starts each argv under exec setsid --wait and records its actual process-group id plus private status files beneath ctx.e2b.runtimeRoot/processes. The handle waits for that file instead of treating the SDK command PID as its published identity. Termination signals the negative recorded id with SIGTERM, waits the caller's graceMs, then escalates to SIGKILL and the SDK kill fallback; TERM delivery or probe failures also force that escalation. Process-table probes treat groups containing only zombie or dead entries as quiescent. Force cleanup succeeds only after a bounded probe finds the group empty; otherwise waitForExit() exposes a retryable failure, while proven quiescence makes later termination a no-op. Publication and monitoring failures apply the same cleanup transaction before rejecting. Service disposal rejects new starts, terminates and joins every retained process group, then awaits SDK settlement and private cleanup before the sandbox owner disposes.
  • Environment boundary — one trusted control-shell probe resolves the sandbox user's login home from its passwd entry and transports the sandbox environment as base64 ASCII for one strict UTF-8 decode; the wrapper then removes ambient DSH_* and credential-shaped (*KEY*, *SECRET*, *TOKEN*) names and restores every valid spec.env entry as an explicit caller opt-in. Empty names, =, and NUL framing violations reject before launch. Subsequent E2B command and PTY login shells receive a fresh randomized root-level HOME plus empty overrides for every scrubbed ambient name before user profiles can run; the requested argv receives the serialized environment afterward without changing the sandbox user's umask. Host ambient variables never enter the sandbox implicitly. Private environment files are removed after consumption, and failed command or terminal setup removes its private state before rejecting.
  • Stdio projection — the remote wrapper branches raw bytes into optional bounded spill files, frames each live chunk as newline-delimited base64 ASCII, and the host incrementally restores bytes across arbitrary SDK callback boundaries. Pipe mode writes those bytes to host Node streams; inherit mode writes them to the harness process streams; collect mode retains a bounded host tail with offset reads. The wrapper publishes the direct command status before waiting for inherited writers. For collect or inherit output, the adapter disconnects an incomplete SDK stream after graceMs, withholds its partial spill, and returns that status while retaining the remote group for waitForExit() and termination. Natural raw-pipe completion instead awaits lossless transport and preserves backpressure; explicit termination destroys the host pipes and releases blocked output before remote cleanup. Batch and streaming stdin use the SDK handle.
  • Terminal sessionsspawnTerminal() uses E2B's byte PTY API, installs the exact argv and scrubbed environment through private mode-0600 files, reports the foreground process group, sends real signals, and tears down every live group in the remote terminal session through one retryable awaited terminate(); termination rejects new handle operations, aborts and joins in-flight writes, inspections, and signals, and treats zombie-only groups as quiescent. A private random output boundary discards the E2B bootstrap shell's prompt and echoed runner command while preserving every requested-process byte, including its first prompt. Terminal output is pushed to the handle's stream without awaiting host backpressure: a flowing consumer (the PTY backend attaches one at construction) folds bytes into its own bounded state, while a paused consumer buffers in host memory. PTY allocation is awaited through handle publication before cancellation is observed, so owned rollback can clean the published handle. Setup and teardown own the private state transaction, abort pending setup during service disposal, and fence publication; sandbox disposal or timeout bounds a setup rollback that also fails. Prompt detection, scrollback, readiness, and owner policy remain in dsh-terminal-bash.
  • Sandbox disappearanceSandboxNotFoundError during process or terminal liveness, termination, rollback, or disconnect proves the remote execution world cannot retain work, so cleanup treats it as quiescent; unrelated failures remain observable.

The default E2B base image supplies the runtime and Bash/GNU utilities this adapter invokes: node, bash, setsid, ps, awk, tr, env, base64, chmod, tee, head, rm, kill, id, and getent.

Model Experience

Indirectly, through Consumers such as the Bash executor behind dsh-tool-bash, which render remote output, exit facts, background deltas, and spill paths.

KV Cache effect

No direct invalidation; the named consumers own any request-prefix changes.

Known Limitations and Deferred Work

  • The SDK still retains complete command output in host memory — E2B CommandHandle.stdout and .stderr accumulate the base64 transport even when this adapter exposes bounded raw-byte tails, so the subprocess seam's normal host-memory bound is not achieved and transport retention is larger than the source stream.
  • Synchronous-PID consumers are unsupportedpid remains -1 during remote startup; consumers that require a positive PID immediately, including the ACP child backend, cannot use this provider unchanged.
  • Private state lives for the sandbox lifetime — process directories and valid spill files remain under .dsh-e2b until the owner deletes the sandbox; this POC supplies no in-sandbox sweep.
  • Control state shares the sandbox user's UID — E2B runs every command as the same default user, so 0700/0600 modes cannot isolate .dsh-e2b control files from concurrently running sandbox processes. A background process could rewrite pid/exit-code or read a not-yet-consumed environment file. The adapter validates published values and refuses group ids whose negative form is unsafe to signal (<= 1), but real isolation needs an E2B per-command user or an out-of-band control channel.
  • Numeric process identities are not reuse-fenced — E2B exposes numeric PID/PGID PTY input, signalling, and cleanup operations but no atomic identity-bound alternative. The adapter minimizes host round trips and live coverage exercises the reproducible stale-interrupt overlap; replacement is deferred until E2B adds an identity primitive or a failure demonstrates a narrower protocol.
  • The initial environment probe inherits sandbox defaults — E2B merges command overrides with default environment entries, so the probe cannot blank unknown credential-shaped names before enumerating them. A same-UID untrusted process already in the sandbox could inspect that short-lived control shell; this POC therefore does not support secrets in sandbox-default environment variables and requires an E2B replacement-environment primitive to close the gap.
  • E2B exposes no signal fact — an adapter-requested SIGTERM or SIGKILL is reported only when no wrapper-published direct exit code wins; every unrequested SDK exit remains an exit code, including values equal to 128 + signal.
  • Exact terminal stdin-wait inspection is unavailable — E2B exposes the foreground process group but not the syscall evidence needed to prove it is waiting on fd 0, so the generic PTY backend falls back to controlled prompt markers and bounded silence.
  • Linux utility and E2B transport semantics are assumed — there is no Windows, escaped-session recovery, or network-partition fidelity layer.

LIMITATIONS

Known limitations

- **The SDK still retains complete command output in host memory** — E2B `CommandHandle.stdout` and `.stderr` accumulate the base64 transport even when this adapter exposes bounded raw-byte tails, so the subprocess seam's normal host-memory bound is not achieved and transport retention is larger than the source stream. - **Synchronous-PID consumers are unsupported** — `pid` remains `-1` during remote startup; consumers that require a positive PID immediately, including the ACP child backend, cannot use this provider unchanged. - **Private state lives for the sandbox lifetime** — process directories and valid spill files remain under `.dsh-e2b` until the owner deletes the sandbox; this POC supplies no in-sandbox sweep. - **Control state shares the sandbox user's UID** — E2B runs every command as the same default user, so `0700`/`0600` modes cannot isolate `.dsh-e2b` control files from concurrently running sandbox processes. A background process could rewrite `pid`/`exit-code` or read a not-yet-consumed `environment` file. The adapter validates published values and refuses group ids whose negative form is unsafe to signal (`<= 1`), but real isolation needs an E2B per-command user or an out-of-band control channel. - **Numeric process identities are not reuse-fenced** — E2B exposes numeric PID/PGID PTY input, signalling, and cleanup operations but no atomic identity-bound alternative. The adapter minimizes host round trips and live coverage exercises the reproducible stale-interrupt overlap; replacement is deferred until E2B adds an identity primitive or a failure demonstrates a narrower protocol. - **The initial environment probe inherits sandbox defaults** — E2B merges command overrides with default environment entries, so the probe cannot blank unknown credential-shaped names before enumerating them. A same-UID untrusted process already in the sandbox could inspect that short-lived control shell; this POC therefore does not support secrets in sandbox-default environment variables and requires an E2B replacement-environment primitive to close the gap. - **E2B exposes no signal fact** — an adapter-requested `SIGTERM` or `SIGKILL` is reported only when no wrapper-published direct exit code wins; every unrequested SDK exit remains an exit code, including values equal to `128 + signal`. - **Exact terminal stdin-wait inspection is unavailable** — E2B exposes the foreground process group but not the syscall evidence needed to prove it is waiting on fd 0, so the generic PTY backend falls back to controlled prompt markers and bounded silence. - **Linux utility and E2B transport semantics are assumed** — there is no Windows, escaped-session recovery, or network-partition fidelity layer.