A Python port of the Anthropic Sandbox Runtime, providing lightweight OS-level sandboxing for arbitrary processes without requiring a container.
py-sandboxrt 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.
Install from PyPI using pip:
pip install py-sandboxrtOr using uv:
uv add py-sandboxrtClone the repository and install in development mode:
git clone https://github.com/saolalab/py-sandboxrt.git
cd py-sandboxrt
pip install -e ".[dev]"Or with uv:
uv sync --dev# Run a command in the sandbox
srt echo "hello world"
# With debug logging
srt --debug curl https://example.com
# Custom settings file
srt --settings /path/to/srt-settings.json npm install
# Command string mode
srt -c "curl https://example.com && echo done"import asyncio
from srt import SandboxManager, SandboxRuntimeConfig
from srt.config import NetworkConfig, FilesystemConfig
config = SandboxRuntimeConfig(
network=NetworkConfig(
allowed_domains=["example.com", "api.github.com"],
denied_domains=[],
),
filesystem=FilesystemConfig(
deny_read=["~/.ssh"],
allow_write=[".", "/tmp"],
deny_write=[".env"],
),
)
async def main():
mgr = SandboxManager()
await mgr.initialize(config)
sandboxed_cmd = await mgr.wrap_with_sandbox("curl https://example.com")
import subprocess
proc = subprocess.Popen(sandboxed_cmd, shell=True)
proc.wait()
mgr.cleanup_after_command()
await mgr.reset()
asyncio.run(main())By default, py-srt reads ~/.srt-settings.json. Override with --settings:
srt --settings /path/to/config.json <command>The config file uses the same JSON format as the TypeScript version (camelCase keys are automatically converted):
{
"network": {
"allowedDomains": [
"github.com",
"*.github.com",
"npmjs.org",
"*.npmjs.org"
],
"deniedDomains": ["malicious.com"]
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowWrite": [".", "src/", "test/", "/tmp"],
"denyWrite": [".env", "config/production.json"]
}
}All network access is denied by default.
| Key | Description |
|---|---|
allowedDomains |
Domains permitted to connect to (supports *.example.com wildcards) |
deniedDomains |
Domains explicitly blocked (checked first, overrides allow) |
allowUnixSockets |
macOS only: specific Unix socket paths to allow |
allowAllUnixSockets |
Allow all Unix sockets on both platforms |
allowLocalBinding |
Allow binding to local ports (default: false) |
| Key | Pattern | Description |
|---|---|---|
denyRead |
deny-only | Paths to block reading (default: allow all reads) |
allowWrite |
allow-only | Paths to allow writing (default: deny all writes) |
denyWrite |
deny-within-allow | Paths to block writing even within allowed paths |
Certain sensitive files are always blocked from writes, even within allowed paths:
- Shell configs:
.bashrc,.bash_profile,.zshrc,.zprofile,.profile - Git configs:
.gitconfig,.gitmodules - Other:
.ripgreprc,.mcp.json - Directories:
.vscode/,.idea/,.claude/commands/,.claude/agents/,.git/hooks/
- macOS: Uses
sandbox-execwith dynamically generated Seatbelt profiles - Linux: Uses
bubblewrapfor containerized filesystem and network namespace isolation
HTTP and SOCKS5 proxy servers run on the host, filtering all traffic against domain allowlists:
- HTTP/HTTPS — intercepted by an HTTP proxy that validates
CONNECTtunnels and forwards requests - Other TCP — handled by a SOCKS5 proxy for SSH, database connections, etc.
- Linux bridge —
socatrelays between Unix sockets (inside sandbox) and TCP ports (host proxies)
- Read (deny-only): All reads allowed by default; deny specific paths
- Write (allow-only): All writes denied by default; explicitly allow paths
- macOS uses Seatbelt regex/glob matching; Linux uses
bubblewrapbind mounts
src/srt/
├── __init__.py # Public API exports
├── cli.py # CLI entrypoint (srt command)
├── config.py # Pydantic configuration models
├── sandbox_manager.py # Main orchestrator
├── http_proxy.py # HTTP/HTTPS proxy with domain filtering
├── socks_proxy.py # SOCKS5 proxy with domain filtering
├── macos_sandbox.py # macOS sandbox-exec Seatbelt profiles
├── linux_sandbox.py # Linux bubblewrap sandboxing
├── sandbox_utils.py # Shared utilities (paths, globs, env vars)
├── violation_store.py # In-memory violation tracking
├── platform_utils.py # Platform detection
└── debug.py # Debug logging
| Platform | Mechanism | Dependencies |
|---|---|---|
| macOS | sandbox-exec + Seatbelt profiles |
ripgrep |
| Linux | bubblewrap + network namespaces |
bubblewrap, socat, ripgrep |
| Windows | Not supported | — |
macOS:
brew install ripgrepLinux (Debian/Ubuntu):
sudo apt-get install bubblewrap socat ripgrepSet the SRT_DEBUG environment variable to enable verbose logging to stderr:
SRT_DEBUG=1 srt curl https://example.com
# or
srt --debug curl https://example.comApache-2.0 — see LICENSE for details.
Python port of anthropic-experimental/sandbox-runtime.