Anthropic sandbox-runtime (srt), Explained — and How to Harden It (Oct 2026)
sandbox-runtime (srt) is Anthropic's lightweight, open-source tool that enforces filesystem and network restrictions on AI agents and arbitrary CLI processes using OS primitives — Seatbelt on macOS, bubblewrap on Linux — rather than a full container or VM. It is the sandbox Claude Code builds on, it is deny-by-default for writes and network but open-by-default for reads, and Anthropic is explicit that it is not a complete isolation boundary.
Your AI agent can run shell commands, install packages and reach the network on your behalf. So what stops a prompt-injected or compromised agent from reading your SSH keys and POSTing them to an attacker's server? In October 2026 Anthropic open-sourced the tool it built for exactly that question: sandbox-runtime, CLI name srt. This post explains what it is, how it works, where it is honest about its own limits, and ends with a concrete hardening checklist you can apply today.
What srt is
srt is, in its README's own words, "a lightweight sandboxing tool for enforcing filesystem and network restrictions on arbitrary processes at the OS level, without requiring a container." Anthropic describes it as "a research preview developed for Claude Code to enable safer AI agents." The Claude Code documentation confirms the relationship verbatim: "Claude Code builds the sandbox on the open source @anthropic-ai/sandbox-runtime package."
- Package:
@anthropic-ai/sandbox-runtimeon npm; CLI:srt. - Install:
npm install -g @anthropic-ai/sandbox-runtime. Linux also needsbubblewrapandsocat;ripgrepis used for deny-path detection. - License: Apache-2.0.
- Status: Beta Research Preview ("APIs and configuration formats may evolve"); Windows is alpha.
It is not limited to Claude Code. The README states it "can be used to sandbox the behaviour of agents, local MCP servers, bash commands and arbitrary processes." Usage is simply srt <command> — for example srt npm install or srt --debug curl https://example.com — and there is a library API (SandboxManager.initialize() then wrapWithSandbox()). Crucially, restrictions "cover any scripts, programs, or subprocesses that are spawned by the command," so child processes inherit the sandbox.
Primary sources, all worth reading: the sandbox-runtime GitHub repository, the Anthropic engineering blog on Claude Code sandboxing, and the Claude Code sandboxing docs.
How it works
srt leans on native OS primitives rather than a virtual machine. The README: it "uses native OS sandboxing primitives (sandbox-exec on macOS, bubblewrap on Linux) and proxy-based network filtering."
| Platform | Mechanism |
|---|---|
| macOS | sandbox-exec with dynamically generated Seatbelt profiles; the profile allows communication only to a specific localhost proxy port. |
| Linux | bubblewrap with network-namespace isolation. The sandboxed process's network namespace is removed entirely, so all traffic must go through host-side proxies reached over Unix sockets bind-mounted into the sandbox. Unix-socket blocking uses seccomp filters (x86-64/arm64 only). |
| Windows | Alpha. A dedicated srt-sandbox local user account, a Windows Filtering Platform egress fence keyed on that account's SID, a write-restricted token, and ACLs on the work tree. Gappier than macOS/Linux — see Limitations. |
Network: deny-by-default, enforced off-box
The README is blunt: "By default, all network access is denied. You must explicitly allow domains." Enforcement does not live inside the sandbox — it lives in two proxies running on the host: an HTTP proxy for HTTP/HTTPS and a SOCKS5 proxy for other TCP. A connection is evaluated in order: a deniedDomains match denies; else an allowedDomains match allows; else, under strictAllowlist, it is denied; else a per-command list is consulted; else an "ask" callback decides (no callback means denied). In Claude Code, that "ask" is the permission prompt.
There is also a resolved-address safety net: allowed hostnames are resolved and any address in a denied set — loopback, link-local, multicast, cloud-metadata endpoints (169.254.169.254), and the host's own interface addresses — is dropped. That is a built-in SSRF and metadata-exfiltration guard.
Filesystem: writes closed, reads open
This is the single most important thing to internalize. With no settings file, the default posture is "no network access, no writes outside the default write paths, and unrestricted reads." The precedence rules:
- Reads are deny-then-allow — open by default. "By default, read access is allowed everywhere."
allowReadtakes precedence overdenyRead. - Writes are allow-only — closed by default.
denyWritetakes precedence overallowWrite. - Network is allow-only — closed by default.
Policy lives in a JSON settings file (~/.srt-settings.json by default, or --settings <path>) with network and filesystem sub-objects:
{
"network": {
"allowedDomains": ["github.com", "*.github.com", "npmjs.org", "*.npmjs.org"],
"deniedDomains": ["malicious.com"],
"allowUnixSockets": ["/var/run/docker.sock"],
"allowLocalBinding": false
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowRead": [],
"allowWrite": [".", "src/", "test/", "/tmp"],
"denyWrite": [".env", "config/production.json"]
}
}
One genuinely good behaviour to call out: a settings file that exists but fails validation is a hard error. srt exits rather than silently falling back to defaults — fail-closed, which is what you want.
The security-versus-usability balance
Anthropic frames sandboxing as a way to cut "approval fatigue" — the safety erosion that happens when a human clicks "approve" on every prompt without reading it. The engineering blog reports that sandboxing "safely reduces permission prompts by 84%" in internal usage. Treat that as Anthropic's reported internal figure, from the engineering blog; we have not independently verified it, and it was not found in the portion of the public docs we reviewed.
The tension runs both ways. Too strict, and real workflows break — the README documents several: Jest/Watchman permission errors (srt "jest --no-watchman"); Chromium/Electron, rootless podman and nested bubblewrap failing with EPERM unless allowNestedUserNamespaces: true (which the README warns weakens guarantees); Go tools needing enableWeakerNetworkIsolation to verify TLS against the MITM proxy; and Ubuntu 24.04+ needing an AppArmor adjustment. Too loose, and the sandbox stops meaning anything — the opt-out switches each trade away a guarantee (more below). Anthropic's core design statement captures why both dimensions are mandatory: "Without file isolation, a compromised process could exfiltrate SSH keys or other sensitive files. Without network isolation, a process could escape the sandbox and gain unrestricted network access."
The honest limitations
To Anthropic's credit, the README documents what srt does not protect against. Keep these front of mind — they are where over-trust bites.
- Not a VM, not a complete isolation boundary. The docs say so outright. A kernel-level exploit is out of scope.
- Reads are open by default. You must hand-write deny lists for secrets. Forgetting this is the most likely real-world misconfiguration.
- Content-blind domain filtering. The proxy "does not otherwise inspect the traffic" — "allowing
github.comlets a process push to any repository," and in some cases filtering "may be ... bypass[ed] through domain fronting." - Unix sockets are host-takeover holes. Allowing
/var/run/docker.sock"would effectively grant access to the host system." On Linux without seccomp, Unix sockets are unrestricted (warning only). - Writable-$PATH is a code-execution escalation. Allowing writes to
$PATHdirs, system config or shell rc files "can lead to code execution in different security contexts." And a writable<project>/node_modules/copy ofsrt"can be rewritten by a sandboxed command, and the nextsrtthere runs the result unsandboxed." - The weakening switches remove guarantees.
enableWeakerNetworkIsolationis a "potential data exfiltration vector through the trustd service";allowAppleEventsmeans "the sandbox no longer provides code-execution isolation";allowNestedUserNamespacesmeans the mandatory denies "no longer hold";enableWeakerNestedSandbox"considerably weakens security." - Linux proxy bypass. Routing relies on
HTTP_PROXY/HTTPS_PROXY/ALL_PROXY, which "may be ignored by programs that don't respect these variables." (The mandatory network-namespace enforcement is the stronger lever here.) - Attribution, not a boundary, between concurrent commands. A sandboxed process presenting another live invocation's id gets that invocation's allows — "this is attribution, not a boundary between concurrent commands of one session."
- No structured Linux audit. macOS can tap the sandbox violation log (
log stream --predicate 'process == "sandbox-exec"'); on Linux bubblewrap has no built-in violation reporting and you bolt on strace (strace -f srt <cmd> 2>&1 | grep EPERM).
On denial, srt does three things: it blocks the operation at the OS level (returns EPERM), logs the violation via platform-specific mechanisms, and notifies the user (a permission prompt in Claude Code).
Hardening checklist — 13 concrete controls
Each item maps to a real srt mechanism. This is the "how to keep it secure" list.
- Deny-by-default everywhere you can. Writes and network are already closed — keep them minimal. Never widen to
allowWrite: ["/"]or a blanket domain. - Fix the read-default gap first. Reads are OPEN by default. Explicitly
denyReadsecret stores:~/.ssh,~/.aws,~/.config,.env,.npmrc, cloud creds, SSH/GPG keys, browser profiles. This is the single most important manual step. - Least-privilege filesystem.
allowWriteonly the project tree plus a scratch dir (/tmp). Never allow writes to$PATHdirs, system config, or shell rc files — that is a code-execution escalation path. Keepsrtinstalled globally, not in a project-writablenode_modules/. - Network allowlist discipline. Allowlist exact domains (
registry.npmjs.org,github.com, your API host), not broad wildcards. Remember the allow is coarse:github.comallows push to any repo. For path/method control, use a custom proxy rule set. - Guard SSRF and metadata endpoints. Rely on (and verify) the resolved-address denylist that drops loopback, link-local, multicast and cloud-metadata (169.254.169.254) targets. Never allow a domain that resolves into internal ranges.
- Treat Unix sockets as privilege grants. Never
allowUnixSocketsfordocker.sock(= host takeover) unless you fully accept that. On Linux, confirm seccomp is active (x86-64/arm64) so sockets are actually filtered; a seccomp-unavailable warning means they are NOT. - Don't flip the weakening switches by reflex.
enableWeakerNetworkIsolation,allowAppleEvents,enableWeakerNestedSandbox,allowAllUnixSocketsandallowNestedUserNamespaceseach remove a guarantee. Use only with a documented reason and compensating isolation. - Fail closed, validate config. Lean on
srtexiting on an invalid settings file; keep the policy in version control and lint it in CI. No silent default fallback. - Audit the denials. Pipe strace / log-stream violations somewhere durable (SigNoz, syslog) so a wall of EPERMs is visible, not just a transient prompt. Review what the agent tried to reach.
- Defence in depth — don't treat it as a VM. For untrusted code or high-value hosts, nest
srtinside a container or microVM. OS-primitive confinement does not survive a kernel exploit. - Scope check for Claude Code specifically. The built-in Bash sandbox covers shell commands only; file tools, hooks and local MCP servers run with full access. For one boundary, run the whole Claude Code process under
srt(or in a container/VM). - Pin and review the policy per project. Different repos need different allowlists; a single global
~/.srt-settings.jsondrifts wide. Prefer--settingsper project. - Mind Linux proxy bypass. Env-var proxying can be ignored by non-conforming programs. Prefer the mandatory network-namespace enforcement and watch for tools that open raw sockets.
Bottom line
srt is a welcome, well-documented default: a low-overhead way to put filesystem and network boundaries around an agent without standing up a container for every command, and the only lightweight local tool of its kind that ships a deny-by-default domain allowlist out of the box. But its own docs are clear that it is confinement, not containment. Treat it as the inner layer — fix the reads-open default, keep allowlists tight, audit denials, and nest it inside stronger isolation when the code is genuinely untrusted. For how srt stacks up against bubblewrap, Firejail, Docker, gVisor, Firecracker and the cloud sandbox services, see our agent sandbox comparison.
Ready to practise the decisions these articles describe?
Run a free War Room →