MCPcopy Index your code
hub / github.com/anthropic-experimental/sandbox-runtime

github.com/anthropic-experimental/sandbox-runtime @v0.0.63 sqlite

repository ↗ · DeepWiki ↗ · release v0.0.63 ↗
376 symbols 1,400 edges 79 files 87 documented · 23% 8 cross-repo links
README

Anthropic Sandbox Runtime (srt)

A lightweight sandboxing tool for enforcing filesystem and network restrictions on arbitrary processes at the OS level, without requiring a container.

srt uses native OS sandboxing primitives (sandbox-exec on macOS, bubblewrap on Linux) and proxy-based network filtering. It can be used to sandbox the behaviour of agents, local MCP servers, bash commands and arbitrary processes.

Beta Research Preview

The Sandbox Runtime is a research preview developed for Claude Code to enable safer AI agents. It's being made available as an early open source preview to help the broader ecosystem build more secure agentic systems. As this is an early research preview, APIs and configuration formats may evolve. We welcome feedback and contributions to make AI agents safer by default!

Installation

npm install -g @anthropic-ai/sandbox-runtime

Basic Usage

# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html>  # Request succeeds

$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist  # Request blocked

# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb...  # Current directory access allowed

$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted  # Specific file blocked

Overview

This package provides a standalone sandbox implementation that can be used as both a CLI tool and a library. It's designed with a secure-by-default philosophy tailored for common developer use cases: processes start with minimal access, and you explicitly poke only the holes you need.

Key capabilities:

  • Network restrictions: Control which hosts/domains can be accessed via HTTP/HTTPS and other protocols
  • Filesystem restrictions: Control which files/directories can be read/written
  • Unix socket restrictions: Control access to local IPC sockets
  • Violation monitoring: On macOS, tap into the system's sandbox violation log store for real-time alerts

Example Use Case: Sandboxing MCP Servers

A key use case is sandboxing Model Context Protocol (MCP) servers to restrict their capabilities. For example, to sandbox the filesystem MCP server:

Without sandboxing (.mcp.json):

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem"]
    }
  }
}

With sandboxing (.mcp.json):

{
  "mcpServers": {
    "filesystem": {
      "command": "srt",
      "args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
    }
  }
}

Then configure restrictions in ~/.srt-settings.json:

{
  "filesystem": {
    "denyRead": [],
    "allowWrite": ["."],
    "denyWrite": ["~/sensitive-folder"]
  },
  "network": {
    "allowedDomains": [],
    "deniedDomains": []
  }
}

Now the MCP server will be blocked from writing to the denied path:

> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'

How It Works

The sandbox uses OS-level primitives to enforce restrictions that apply to the entire process tree:

  • macOS: Uses sandbox-exec with dynamically generated Seatbelt profiles
  • Linux: Uses bubblewrap for containerization with network namespace isolation

0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305

Dual Isolation Model

Both filesystem and network isolation are required for effective sandboxing. 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.

Filesystem Isolation enforces read and write restrictions:

  • Read (deny-then-allow pattern): By default, read access is allowed everywhere. You can deny broad regions (e.g., /Users) and then re-allow specific paths within them (e.g., .). allowRead takes precedence over denyRead — the opposite of write, where denyWrite takes precedence over allowWrite.
  • Write (allow-only pattern): By default, write access is denied everywhere. You must explicitly allow paths (e.g., ., /tmp). An empty allow list means no write access.

Network Isolation (allow-only pattern): By default, all network access is denied. You must explicitly allow domains. An empty allowedDomains list means no network access. Network traffic is routed through proxy servers running on the host:

  • Linux: Requests are routed via the filesystem over a Unix domain socket. The network namespace of the sandboxed process is removed entirely, so all network traffic must go through the proxies running on the host (listening on Unix sockets that are bind-mounted into the sandbox)

  • macOS: The Seatbelt profile allows communication only to a specific localhost port. The proxies listen on this port, creating a controlled channel for all network access

Both HTTP/HTTPS (via HTTP proxy) and other TCP traffic (via SOCKS5 proxy) are mediated by these proxies, which enforce your domain allowlists and denylists.

For more details on sandboxing in Claude Code, see:

Architecture

src/
├── index.ts                  # Library exports
├── cli.ts                    # CLI entrypoint (srt command)
├── utils/                    # Shared utilities
│   ├── debug.ts             # Debug logging
│   ├── settings.ts          # Settings reader (permissions + sandbox config)
│   ├── platform.ts          # Platform detection
│   └── exec.ts              # Command execution utilities
└── sandbox/                  # Sandbox implementation
    ├── sandbox-manager.ts    # Main sandbox manager
    ├── sandbox-schemas.ts    # Zod schemas for validation
    ├── sandbox-violation-store.ts # Violation tracking
    ├── sandbox-utils.ts      # Shared sandbox utilities
    ├── http-proxy.ts         # HTTP/HTTPS proxy for network filtering
    ├── socks-proxy.ts        # SOCKS5 proxy for network filtering
    ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing
    └── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing

Usage

As a CLI tool

The srt command (Anthropic Sandbox Runtime) wraps any command with security boundaries:

# Run a command in the sandbox
srt echo "hello world"

# With debug logging
srt --debug curl https://example.com

# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install

As a library

import {
  SandboxManager,
  type SandboxRuntimeConfig,
} from '@anthropic-ai/sandbox-runtime'
import { spawn } from 'child_process'

// Define your sandbox configuration
const config: SandboxRuntimeConfig = {
  network: {
    allowedDomains: ['example.com', 'api.github.com'],
    deniedDomains: [],
  },
  filesystem: {
    denyRead: ['~/.ssh'],
    allowWrite: ['.', '/tmp'],
    denyWrite: ['.env'],
  },
}

// Initialize the sandbox (starts proxy servers, etc.)
await SandboxManager.initialize(config)

// Wrap a command with sandbox restrictions
const sandboxedCommand = await SandboxManager.wrapWithSandbox(
  'curl https://example.com',
)

// Execute the sandboxed command
const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })

// Handle exit and cleanup after child process completes
child.on('exit', async code => {
  console.log(`Command exited with code ${code}`)
  // Cleanup when done (optional, happens automatically on process exit)
  await SandboxManager.reset()
})

Available exports

// Main sandbox manager
export { SandboxManager } from '@anthropic-ai/sandbox-runtime'

// Violation tracking
export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'

// TypeScript types
export type {
  SandboxRuntimeConfig,
  NetworkConfig,
  FilesystemConfig,
  IgnoreViolationsConfig,
  SandboxAskCallback,
  FsReadRestrictionConfig,
  FsWriteRestrictionConfig,
  NetworkRestrictionConfig,
} from '@anthropic-ai/sandbox-runtime'

Configuration

Settings File Location

By default, the sandbox runtime looks for configuration at ~/.srt-settings.json. You can specify a custom path using the --settings flag:

srt --settings /path/to/srt-settings.json <command>

Complete Configuration Example

{
  "network": {
    "allowedDomains": [
      "github.com",
      "*.github.com",
      "lfs.github.com",
      "api.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"]
  },
  "ignoreViolations": {
    "*": ["/usr/bin", "/System"],
    "git push": ["/usr/bin/nc"],
    "npm": ["/private/tmp"]
  },
  "enableWeakerNestedSandbox": false,
  "enableWeakerNetworkIsolation": false,
  "allowAppleEvents": false
}

Configuration Options

Network Configuration

Uses an allow-only pattern - all network access is denied by default.

  • network.allowedDomains - Array of allowed domains (supports wildcards like *.example.com). Empty array = no network access.
  • network.deniedDomains - Array of denied domains (checked first, takes precedence over allowedDomains)
  • network.allowLocalBinding - Allow binding to local ports (boolean, default: false)

TLS termination (network.tlsTerminate, experimental): when set, HTTPS CONNECTs are terminated in-process so SRT can see (and filter, via network.filterRequest) the decrypted requests. The sandboxed process is pointed at a trust bundle containing the MITM CA (caCertPath/caKeyPath, or an ephemeral CA if omitted) plus the host's regular roots, so proxy-minted certificates and real upstream certificates both verify.

  • network.tlsTerminate.excludeDomains - Domain patterns (same syntax as allowedDomains) that are not terminated. Matching CONNECTs are tunnelled opaquely instead: they are still subject to the domain allowlist, but the client inside the sandbox completes its own TLS handshake with the real upstream, and filterRequest / credential injection do not apply to their HTTPS traffic. Use this for the two cases TLS termination fundamentally breaks:
  • mTLS upstreams - only the in-sandbox client holds the client certificate, so the proxy cannot re-originate the connection on its behalf.
  • Certificate-pinning clients - clients that verify the upstream's identity themselves (custom CAs, SAN pinning) and reject the MITM certificate.
  • network.tlsTerminate.extraCaCertPaths - Paths to PEM CA certificate files appended to that trust bundle, after the MITM CA and the host's regular roots. Excluded (non-terminated) hosts are verified by the client inside the sandbox, and the trust env vars SRT sets (SSL_CERT_FILE, GIT_SSL_CAINFO, ...) replace each tool's own trust configuration, so a site-local root (e.g. an internal mTLS CA) must be in the bundle or those hosts can never be verified. Only the CERTIFICATE blocks of each file are copied into the bundle (anything else, e.g. a private key in a combined PEM, is never exposed to the sandbox); files that are missing, unreadable, or contain no PEM CERTIFICATE block are skipped, so it is safe to list paths that exist on only some hosts.
{
  "network": {
    "allowedDomains": ["*.example.com", "internal-mtls.example.net"],
    "deniedDomains": [],
    "tlsTerminate": {
      "excludeDomains": ["internal-mtls.example.net"],
      "extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
    }
  }
}

Unix Socket Settings (platform-specific behavior):

Setting macOS Linux
allowUnixSockets: string[] Allowlist of socket paths Ignored (seccomp can't filter by path)
allowAllUnixSockets: boolean Allow all sockets Disable seccomp blocking

Unix sockets are blocked by default on both platforms.

  • macOS: Use allowUnixSockets to allow specific paths (e.g., ["/var/run/docker.sock"]), or allowAllUnixSockets: true to allow all.
  • Linux: Blocking uses seccomp filters (x64/arm64 only). If seccomp isn't available, sockets are unrestricted and a warning is shown. Use allowAllUnixSockets: true to explicitly disable blocking.

Filesystem Configuration

Uses two different patterns:

Read restrictions (deny-then-allow pattern) - all reads allowed by default:

  • filesystem.denyRead - Array of paths to deny read access. Empty array = full read access.
  • filesystem.allowRead - Array of paths to re-allow read access within denied regions (takes precedence over denyRead). Note: this is the opposite of write, where denyWrite takes precedence over allowWrite.

Write restrictions (allow-only pattern) - all writes denied by default:

  • filesystem.allowWrite - Array of paths to allow write access. Empty array = no write access.
  • filesystem.denyWrite - Array of paths to deny write access within allowed paths (takes precedence over allowWrite)

Path Syntax (macOS):

Paths support git-style glob patterns on macOS, similar to .gitignore synt

Extension points exported contracts — how you extend this code

ReplacementSpan (Interface)
A `[start, end)` slice of the original content to replace.
src/sandbox/credential-mask-files.ts
BoundListener (Interface)
(no doc)
test/sandbox/winsrt.test.ts
RipgrepConfig (Interface)
(no doc)
src/utils/ripgrep.ts
ExpandGlobOptions (Interface)
(no doc)
src/sandbox/sandbox-utils.ts
MuxProxyOptions (Interface)
(no doc)
src/sandbox/mux-proxy.ts
MuxProxyServer (Interface)
(no doc)
src/sandbox/mux-proxy.ts
FsReadRestrictionConfig (Interface)
(no doc)
src/sandbox/sandbox-schemas.ts

Core symbols most depended-on inside this repo

logForDebugging
called by 163
src/utils/debug.ts
wrapWithSandbox
called by 133
src/sandbox/sandbox-manager.ts
reset
called by 124
src/sandbox/sandbox-manager.ts
initialize
called by 94
src/sandbox/sandbox-manager.ts
wrapCommandWithSandboxMacOS
called by 66
src/sandbox/macos-sandbox-utils.ts
spawnAsync
called by 63
test/helpers/spawn.ts
close
called by 60
src/sandbox/mux-proxy.ts
wrapCommandWithSandboxLinux
called by 53
src/sandbox/linux-sandbox-utils.ts

Shape

Function 273
Method 60
Interface 37
Class 6

Languages

TypeScript100%

Modules by API surface

src/sandbox/sandbox-manager.ts86 symbols
src/sandbox/windows-sandbox-utils.ts32 symbols
src/sandbox/parent-proxy.ts23 symbols
src/sandbox/sandbox-utils.ts18 symbols
src/sandbox/linux-sandbox-utils.ts18 symbols
test/sandbox/winsrt.test.ts14 symbols
src/sandbox/mux-proxy.ts13 symbols
src/sandbox/macos-sandbox-utils.ts12 symbols
src/sandbox/credential-mask-files.ts12 symbols
src/sandbox/credential-sentinel.ts11 symbols
src/sandbox/sandbox-violation-store.ts10 symbols
src/sandbox/tls-terminate-proxy.ts9 symbols

Dependencies from manifests, versioned

@eslint/js9.14.0 · 1×
@pondwader/socks5-server1.0.10 · 1×
@types/bun1.3.2 · 1×
@types/node18 · 1×
@types/node-forge1.3.14 · 1×
commander12.1.0 · 1×
eslint9.14.0 · 1×
eslint-import-resolver-typescript3.6.3 · 1×
eslint-plugin-n17.16.2 · 1×

For agents

$ claude mcp add sandbox-runtime \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact