A Rust implementation of a Linux sandbox that runs commands in isolated environments with namespace separation, resource limits, and optional stateful sessions.
Coding agent creating a PR in an isolated sandbox:
demooooo.mp4
- PID namespace isolation - sandboxed processes can't see host processes
- Mount namespace isolation - sandboxed processes have their own filesystem view
- Chroot jail - processes are confined to a minimal filesystem
- Resource limits - CPU time, memory, file size, open files
- Stateful sessions - files and environment variables persist across requests
- HTTP API - easy integration with any language/tool
- gRPC API - high-performance binary protocol for low-latency operations
- Python SDK - native Python client with async support
# Build and run with Docker
docker compose up --build
# Test it
curl -X POST http://localhost:8080/run \
-H "Content-Type: application/json" \
-d '{"command": ["/bin/echo", "Hello from sandbox!"]}'POST /run - Run a command in a fresh sandbox (cleaned up after)
curl -X POST http://localhost:8080/run \
-H "Content-Type: application/json" \
-d '{
"command": ["git", "--version"],
"time": 5000,
"mem": 2097152,
"fsize": 10240,
"nofile": 64
}'Response:
{
"stdout": "git version 2.39.2\n",
"stderr": "",
"exit_code": 0,
"signal": null
}Sessions preserve files and environment variables across multiple requests.
POST /sessions - Create a new session
curl -X POST http://localhost:8080/sessions \
-H "Content-Type: application/json" \
-d '{"env": {"MY_VAR": "hello"}}'
# Returns: {"session_id": "uuid..."}POST /sessions/:id/run - Run command in session
# Write a file
curl -X POST http://localhost:8080/sessions/{id}/run \
-H "Content-Type: application/json" \
-d '{"command": ["/bin/sh", "-c", "echo hello > /tmp/test.txt"]}'
# Read it back (file persists!)
curl -X POST http://localhost:8080/sessions/{id}/run \
-H "Content-Type: application/json" \
-d '{"command": ["/bin/cat", "/tmp/test.txt"]}'POST /sessions/:id/env - Set environment variables
curl -X POST http://localhost:8080/sessions/{id}/env \
-H "Content-Type: application/json" \
-d '{"env": {"GH_TOKEN": "..."}}'POST /sessions/:id/cwd - Set working directory
curl -X POST http://localhost:8080/sessions/{id}/cwd \
-H "Content-Type: application/json" \
-d '{"cwd": "/tmp"}'GET /sessions - List all sessions
GET /sessions/:id - Get session info
DELETE /sessions/:id - Delete session and cleanup
GET /health - Returns "OK"
| Parameter | Default | Description |
|---|---|---|
time |
5000 | CPU time limit in milliseconds |
mem |
2097152 | Memory limit in KB (2GB default for Go programs) |
fsize |
10240 | Max file size in KB |
nofile |
64 | Max open files |
env |
{} | Environment variables |
cwd |
"/" | Working directory |
The binary also supports direct CLI execution:
sudo ./opensandbox --run --time 1000 --mem 262144 -- /bin/echo "hello"The sandbox includes:
- Standard Unix utilities (
/bin,/usr/bin) gitgh(GitHub CLI)
HTTP Request (8080) gRPC Request (50051)
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Axum Server │ │ Tonic Server │
└───────┬───────┘ └───────┬───────┘
│ │
└──────────┬───────────────────────┘
│
▼
┌─────────────────┐
│ Shared State │ (Sessions, Sandbox roots)
└────────┬────────┘
│
▼
┌─────────────────┐
│ spawn_blocking │ (tokio blocking task)
└────────┬────────┘
│
▼
┌─────────────────┐
│ clone() with │ CLONE_NEWPID | CLONE_NEWNS
│ namespaces │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Child Process │
│ - chroot │
│ - setrlimit │
│ - execvpe │
└─────────────────┘
- Requires
--privilegedDocker flag for namespace operations - Processes run as root inside sandbox (privilege dropping disabled due to multi-threading issues)
- Sandbox isolation via: PID namespace, mount namespace, chroot, resource limits
- No network namespace isolation (processes can access network)
# Requires Linux
cargo build --release
# Run (requires root)
sudo ./target/release/opensandbox serve --port 8080- Sessions auto-expire after 5 minutes of inactivity
- Expired sessions are cleaned up automatically
- Each session has its own sandbox directory at
/tmp/sandbox-{id}
Fly.io runs apps in Firecracker VMs, which provides the necessary privileges for namespace operations.
- Install the Fly CLI and login:
curl -L https://fly.io/install.sh | sh
fly auth login- Create
fly.tomlin the project root:
app = "opensandbox"
primary_region = "ord"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 1
[checks]
[checks.health]
type = "http"
port = 8080
path = "/health"
interval = "10s"
timeout = "2s"
[[vm]]
memory = "2gb"
cpu_kind = "shared"
cpus = 2- Deploy:
fly launch- Allocate a public IP (if not automatically assigned):
fly ips allocate-v4 --shared- Test:
curl https://your-app-name.fly.dev/healthSee benchmarks/ for full performance comparisons with E2B.
Summary (OpenSandbox gRPC vs E2B Cloud):
| Metric | OpenSandbox | E2B | Winner |
|---|---|---|---|
| Sandbox creation | 120ms | 232ms | OpenSandbox 1.9x faster |
| Command execution | 28ms | 52ms | OpenSandbox 1.9x faster |
| File write | 10ms | 34ms | OpenSandbox 3.4x faster |
| File read | 9ms | 50ms | OpenSandbox 5.5x faster |
| Git clone workflow | 756ms | 1322ms | OpenSandbox 1.7x faster |
| Concurrency (8x) | 27.5/sec | 11.8/sec | OpenSandbox 2.3x faster |
OpenSandbox wins every category with the gRPC SDK and edge deployment.
Install the SDK for high-performance gRPC access:
pip install opensandbox # or: pip install -e sdk/pythonUsage:
from opensandbox import OpenSandbox
async with OpenSandbox("https://your-server.fly.dev") as client:
sandbox = await client.create()
# Run commands (fast - uses gRPC)
result = await sandbox.run("echo hello")
print(result.stdout)
# File operations (fast - native, no shell)
await sandbox.write_file("/tmp/test.py", "print('hello')")
content = await sandbox.read_file_text("/tmp/test.py")
await sandbox.destroy()See sdk/python/README.md for full documentation.
- isolate - Sandbox used by the International Olympiad in Informatics (IOI)