Sandbox SDK integrations (Tensorlake / E2B / Modal / Daytona)
May 6, 2026 · View on GitHub
When agentsh runs as a service that supervises commands spawned by a sandbox SDK,
the spawned commands are siblings of the agentsh server, not descendants. Kernel
filters loaded on the agentsh server's process (Landlock, seccomp-notify) do not
govern those sibling processes. The shim_install feature closes that gap: the
shell shim installs the same filters on its own process before exec'ing the user's
command, so the inherited filter follows the command into whatever process tree the
SDK created.
The problem
Sandbox SDKs such as Tensorlake, E2B, Modal, and Daytona spawn agent commands directly — typically via a container exec or a remote shell API call. The spawned shell is a sibling of the agentsh server process, not a child. Because Linux Landlock and seccomp-notify filters are inherited only down the fork/exec chain, none of the file, network, or signal rules configured in the agentsh session apply to those commands. An agent running in this pattern has no kernel-enforced policy at all.
The fix
The agentsh shell shim (agentsh-shell-shim) intercepts every command invocation.
With shim_install enabled, the shim calls the agentsh server's wrap-init
endpoint before exec'ing the user's shell, installs the session's Landlock and
seccomp filters on its own process, and then hands control to the user's command.
Because the filters are installed before execve, the user's command inherits them
regardless of which process tree it is in.
This reuses the existing agentsh-unixwrap machinery. No new kernel interfaces are
required. For the full design, see
docs/superpowers/specs/2026-05-02-shim-kernel-enforcement-design.md.
Configuration
The trusted source is /etc/agentsh/shim.conf (root-owned, admin-managed):
shim_install=auto # auto | on | off (default: auto)
| Value | Behavior |
|---|---|
auto | Shim calls wrap-init and installs when the server returns a populated wrapper response. Falls through to the existing agentsh-exec proxy only when wrap-init itself fails (server unreachable, 5xx, network error) — not after the wrapper has launched. |
on | Shim must install. Any wrap-init failure or empty response exits 126 with a hint pointing to this doc. |
off | Shim never attempts install. Equivalent to pre-#267 behavior. |
Once the shim has launched agentsh-unixwrap as a child, the wrapper's exit code
is terminal in both auto and on mode — there is no fall-through after that point.
Environment variable override: AGENTSH_SHIM_INSTALL=auto|on|off
The env var may only strengthen enforcement, never weaken it. If the env var
would produce a weaker mode than /etc/agentsh/shim.conf (e.g., config says on
and env says off), the env var is silently ignored and the config wins. This
prevents a malicious sandbox-SDK supervisor from pre-setting the env var to bypass
enforcement.
What happens per shim invocation
Decision tree:
shim_install=off? → skip (fall through to agentsh-exec proxy)
↓
Call wrap-init
↓
wrap-init error?
mode=auto → skip (server unreachable / 5xx)
mode=on → exit 126 with hint
↓
WrapperBinary or NotifySocket empty?
mode=auto → skip
mode=on → exit 126 (empty response treated as failure)
↓
Install proceeds (relay + exec)
Relay protocol:
- Shim calls
wrap-initand receivesWrapperBinary+NotifySocketpath. - Shim creates an AF_UNIX SOCK_SEQPACKET socketpair. Parent end stays in the shim; child end becomes fd 3 in the wrapper.
- Sets
AGENTSH_NOTIFY_SOCK_FD=3in the wrapper environment. - Launches
agentsh-unixwrapas a child process (notsyscall.Exec). The shim stays alive as the parent so sandbox toolboxes that track the spawned PID's output do not lose it. - Shim acts as relay: receives the seccomp notify fd from the wrapper via
SCM_RIGHTS on the parent socketpair end, dials the server's NotifySocket,
forwards the notify fd, then sends the ACK byte back through the socketpair.
The wrapper's
waitForACKunblocks. agentsh-unixwrapapplies Landlock, then exec's the user's shell.- The user's command runs under both filters. The shim waits for the wrapper child
to exit and propagates its exit code via
os.Exit.
Nested shim invocations (bash -c "bash -c ...") used to install at every level
and rely on filter stacking up to the kernel's 64-filter limit. As of v0.19.2
(#288), the shim's kernelinstall path consults /proc/self/status
Seccomp_filters: and skips wrap-init when a filter has already been inherited
from an ancestor — that count is kernel-maintained and unforgeable from
userland, unlike env-var markers. The first invocation in a session installs;
nested shells inherit and short-circuit. This avoids the stacking failure mode
that surfaced as masked ECANCELED (real EFAULT) on Runloop devboxes (#282).
Env-var markers are still not used; the kernel-side filter count is the
authority.
Limitations
-
Signal filter rules not enforced in shim mode. When the session has signal-filter rules enabled,
WrapperEnvincludesAGENTSH_SIGNAL_SOCK_FD=4. The shim does not openSignalSocketor pass an inherited fd 4, so the shim stripsAGENTSH_SIGNAL_SOCK_FDfrom the wrapper environment. Signal-rule enforcement remains a server-spawned-only feature until a future iteration extends the relay to handle the second socketpair. Operators relying on signal rules must use the server-spawned path. -
Direct SDK exec without bash bypasses the shim. Calls of the form
sb.exec("cat", [...])that invoke the binary directly (without going through the shell shim) are not intercepted. The fix on that path is to integrate the SDK withagentsh execdirectly. Tracked as a separate concern. -
Restricted seccomp environments (Daytona, Fargate, some container LSM profiles). These environments set
no_new_privsor restrictprctl, causing the wrapper's seccomp install to fail with EPERM. Once the shim has committed to install (wrap-init returned a usable response and the wrapper was launched), the wrapper exits non-zero and the shim propagates that exit code as-is in bothmode=autoandmode=on— there is no silent skip. The currentagentsh-unixwrapexits with status1on install failure (not 126); the shim faithfully relays that. To avoid breakage on these environments, setshim_install=offin/etc/agentsh/shim.confand use ptrace-pid mode (#269) instead. -
Per-invocation cost ~5–10 ms (HTTP wrap-init round trip + exec hop + filter install). Acceptable for sandbox-SDK use cases; not recommended for workloads that fork thousands of short-lived commands per second.
See also
- Issue #267: sandbox-SDK sibling-process-tree enforcement gap
- Issue #268: shim_install design and implementation tracking
- Issue #282: nested-shim filter stacking failure on Runloop, fixed by the
/proc/self/statusshort-circuit in #288 (v0.19.2) - Issue #283: unixwrap pre-resolves the command path before installing the
seccomp filter so
exec.LookPathprobes are not intercepted by the file-monitor handler (v0.19.2) - Design spec:
docs/superpowers/specs/2026-05-02-shim-kernel-enforcement-design.md