An isolated execution environment for AI agents made with Firecracker microVMs.
Give any AI agent its own Linux machine. Execute code, install packages, manipulate files, run processes, and access the internet, all inside a isolated microVM that boots in milliseconds and destroyed when the session ends.
AI agents need to do things: write code and run it, install libraries, curl endpoints, spawn background processes, read and write files. But running agent generated code on your host machine is unpredictable, potentially destructive, and impossible to sandbox with containers alone(shared kernel problem).
Agent Sandbox solves this by giving each agent session a dedicated Firecracker microVM:
- Hardware level isolation - each session runs in its own Linux kernel. A misbehaving agent cannot escape to the host or affect other sessions.
- Millisecond startup - pre-snapshotted VM state restores in 1–5ms, so agents don't wait for environments to spin up.
- Full Linux environment - agents get a real filesystem, process table, and network stack.
- Pre-built & Custom Templates - provision sessions with pre-baked Node.js, Python, Go, or custom Dockerfile environments.
- Ephemeral by design - sessions are stateless, time-bounded, and automatically reaped.
┌─────────────────────────────────────────────────────────────┐
│ Host (Linux + KVM) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Express API │ │ MCP Server │ │ Metrics │ │
│ │ /exec/* │ │ stdio / SSE │ │ /metrics │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────────┘ │
│ │ │ │
│ └───────┬───────────┘ │
│ ▼ │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ Session │───►│ Template Registry │ │
│ │ Gateway │ │ (Node, Python, Go, etc.) │ │
│ └──────┬───────┘ └───────────────────────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ VM Manager │ │
│ │ jailer + snapshot │ │
│ │ restore + lifecycle │ │
│ └───────────┬───────────┘ │
│ │ │
│ ┌───────────┴───────────────────────────────┐ │
│ │ Per-VM Network Namespace │ │
│ │ veth pair ── TAP ── NAT / iptables │ │
│ └───────────┬───────────────────────────────┘ │
│ │ vsock │
│ ╔═══════════╧═══════════════════════════════╗ │
│ ║ Firecracker microVM ║ │
│ ║ ║ │
│ ║ ┌─────────────┐ ┌──────────────┐ ║ │
│ ║ │ runtime.js │───│ /workspace │ ║ │
│ ║ │ (Node.js) │ │ (tmpfs) │ ║ │
│ ║ └─────────────┘ └──────────────┘ ║ │
│ ║ │ ║ │
│ ║ socat ◄──► vsock:5000 ║ │
│ ╚═══════════════════════════════════════════╝ │
└─────────────────────────────────────────────────────────────┘
- Session request arrives via the REST API, MCP protocol or client SDK, specifying an optional
template(e.g.node,python,go). - The Session Gateway looks up the pre-built snapshot artifacts from the Template Registry and lazily creates a VM by restoring a snapshotted Firecracker instance in about ~1–5ms.
- Each VM is placed inside its own Linux network namespace with a dedicated veth pair, TAP device, and NAT rules - giving the guest full outbound internet access while remaining isolated from other VMs.
- Commands are sent to the guest runtime over a vsock channel. The runtime executes processes, manipulates the filesystem, and streams results back.
- When a session is idle for 30 minutes (configurable), the session reaper tears down the VM, jail directory, and network namespace.
Each agent session provides:
| Capability | Details |
|---|---|
| Multiple Environments | Pre-built templates for Node.js, Python, Go, and custom Dockerfiles. |
| Execute commands | Run any binary - node, python3, go, sh, curl, etc. Stdout/stderr streamed in real-time. |
| Filesystem access | Read, write, and list files within an isolated /workspace (tmpfs). |
| Install packages | Full network access - npm install, pip install, go get all work. |
| Process management | Per-command timeouts, cancellation via SIGTERM/SIGKILL, exit code tracking. |
| Network access | Each VM has its own network stack with DNS, outbound HTTP/HTTPS, and NAT. |
| Session persistence | Workspace state persists across commands within a session. |
Agent Sandbox supports pre-snapshotted environment templates. Environments boot in milliseconds with pre-installed runtimes and dependencies.
node(Default): Alpine 3.20 + Node.js 22 + npm + git + curlpython: Alpine 3.20 + Python 3.12 + pip + git + curlgo: Alpine 3.20 + Go 1.23 + git + curl
Use the included build pipeline script to build pre-configured or custom templates:
# Build the Node.js template snapshot
sudo ./templates/build.sh node
# Build the Python template snapshot
sudo ./templates/build.sh python
# Build the Go template snapshot
sudo ./templates/build.sh goYou can define custom environment templates by creating a directory under templates/<your-template-name>/ with a Dockerfile:
FROM agent-sandbox-base:latest
# Install custom tools and runtimes
RUN apk add --no-cache ruby rust cargo postgresql-client
LABEL template.name="data-science" \
template.displayName="Data Science & Rust" \
template.tools="ruby,rustc,cargo,psql"Then generate the snapshot:
sudo ./templates/build.sh data-scienceThe build script will:
- Build the Docker rootfs.
- Extract the filesystem image into an ext4 rootfs.
- Provision a Firecracker jail and boot the guest VM until
READY. - Create the Firecracker snapshot state and write
template.jsonmetadata to/var/lib/agent-sandbox/artifacts/templates/<name>/.
Agent Sandbox provides three integration layers - SDKs, REST API and MCP server.
Install a typed client library and start running code in two lines.
npm install @agent-sandbox/sdkimport { Sandbox } from "@agent-sandbox/sdk";
const sandbox = new Sandbox();
const session = sandbox.create({ template: "python" });
const result = await session.runCode("print(2 + 2)");
console.log(result.output[0].data); // "4\n"
// Stream output in real-time
for await (const chunk of session.execStream("npm", { args: ["test"] })) {
if (chunk.type === "stream") process.stdout.write(chunk.data!);
}
// Write + read files
await session.writeFile("data.json", JSON.stringify({ key: "value" }));
const { content } = await session.readFile("data.json");
await session.destroy();Uses native fetch (Node 18+, Deno, Bun).
See sdk/typescript/README.md for the full API reference.
HTTP endpoints for direct integration:
# List available environment templates
curl http://localhost:3000/exec/templates
# Execute a command in a session (specifying template)
curl -X POST http://localhost:3000/exec/session1/execute \
-H "Content-Type: application/json" \
-d '{"template":"python","command":"python3","args":["-c","print(\"hello from python template\")"]}'
# Write a file
curl -X POST http://localhost:3000/exec/session1/write \
-H "Content-Type: application/json" \
-d '{"path":"main.py","content":"print(\"hello\")"}'
# Read a file
curl http://localhost:3000/exec/session1/read?path=main.py
# List files
curl http://localhost:3000/exec/session1/files?recursive=true
# Destroy a session
curl -X DELETE http://localhost:3000/exec/session1
# List all sessions
curl http://localhost:3000/exec/An MCP server to connect any MCP-compatible AI agent (Claude, GPT, custom agents) directly:
| Tool | Description |
|---|---|
create_session |
Provision a new isolated environment (supports template parameter) |
list_templates |
List available environment templates (node, python, go, etc.) |
execute |
Run a command inside the session's VM |
write_file |
Write content to the session workspace |
read_file |
Read a file from the session workspace |
list_files |
List directory contents |
reset_session |
Destroy a session and release resources |
Transports supported:
- SSE - connect over HTTP with Bearer token auth (
/mcpendpoint) - stdio - run as a local MCP server via
npm run mcp
{
"mcpServers": {
"agent-sandbox": {
"command": "node",
"args": ["dist/mcp/stdio.js"]
}
}
}Every session gets defense-in-depth isolation:
| Layer | Mechanism |
|---|---|
| Compute | Dedicated Firecracker microVM with its own Linux kernel |
| Filesystem | Read-only rootfs + ephemeral tmpfs workspace |
| Network | Per-VM Linux network namespace (veth + TAP + NAT) |
| Process | Firecracker Jailer - chroot, UID/GID separation, seccomp |
| Resources | Configurable vCPU, memory, and file descriptor limits |
| Lifecycle | Automatic reaping of idle sessions (default: 30 min TTL) |
| Security | Path traversal prevention on all file operations |
Agent Sandbox supports API key-based authentication with scope-based access control (exec, admin, metrics) and per-key rate limiting.
Use the key management CLI to create, list, rotate, or revoke API keys:
# Create a key with default 'exec' scope
npm run keys create "my-agent-key"
# Create an admin key with custom rate limit (100 req/min)
npm run keys create "admin-key" --scopes exec,admin,metrics --rate-limit 100
# List all keys
npm run keys list
# Rotate a key
npm run keys rotate <key-id>
# Revoke a key
npm run keys revoke <key-id>API keys can be supplied via HTTP headers, query parameters, or the SDK:
# Authorization Header
curl -H "Authorization: Bearer sk_test_..." http://localhost:3000/exec/templates
# X-API-Key Header
curl -H "X-API-Key: sk_test_..." http://localhost:3000/exec/templatesimport { Sandbox } from "@agent-sandbox/sdk";
const sandbox = new Sandbox({
// AUTH_KEY_PREFIX e.g. 'sk_test_' is read from environment variables
apiKey: `${process.env.AUTH_KEY_PREFIX}example-key`,
});- Linux host with KVM support (
/dev/kvmmust be accessible) - Firecracker and Jailer binaries installed
- Node.js v18+
- Docker (required for template build pipeline)
- Root access (required for Jailer, network namespaces, and iptables)
Download the latest release from firecracker-microvm/firecracker and place both binaries in /usr/local/bin/:
# Example for v1.16.0 (check for the latest version)
ARCH="$(uname -m)"
release_url="https://github.com/firecracker-microvm/firecracker/releases"
latest=$(basename $(curl -fsSLI -o /dev/null -w %{url_effective} ${release_url}/latest))
curl -L ${release_url}/download/${latest}/firecracker-${latest}-${ARCH}.tgz | tar -xz
sudo mv release-${latest}-${ARCH}/firecracker-${latest}-${ARCH} /usr/local/bin/firecracker
sudo mv release-${latest}-${ARCH}/jailer-${latest}-${ARCH} /usr/local/bin/jailer
rm -rf release-${latest}-${ARCH}
# Verify
firecracker --versionThe Jailer runs Firecracker processes under a dedicated unprivileged user. Create the group and user if they don't already exist:
sudo groupadd -g 982 firecracker 2>/dev/null || true
sudo useradd -u 997 -g 982 -M -s /usr/sbin/nologin firecracker 2>/dev/null || trueNote: The default UID/GID (997/982) can be overridden via the
FIRECRACKER_UIDandFIRECRACKER_GIDenvironment variables.
VMs need outbound internet access. Enable kernel IP forwarding:
# Enable now
sudo sysctl -w net.ipv4.ip_forward=1
# Persist across reboots
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ip-forward.confgit clone https://github.com/vivek1504/agent-sandbox.git
cd agent-sandbox
npm installDownload the guest kernel image:
sudo mkdir -p /var/lib/agent-sandbox/artifacts
wget https://github.com/vivek1504/agent-sandbox/releases/download/Beta/vmlinux
sudo mv vmlinux /var/lib/agent-sandbox/artifacts/
sudo chown -R root:firecracker /var/lib/agent-sandbox/artifacts
sudo chmod 750 /var/lib/agent-sandbox/artifactsBuild the default environment templates (node, python, go):
# Build Node.js template
sudo ./templates/build.sh node
# Build Python template
sudo ./templates/build.sh python
# Build Go template
sudo ./templates/build.sh goThe server requires root to manage network namespaces, iptables rules, and the Jailer:
sudo npm start
# → listening on http://localhost:3000# Health check
curl http://localhost:3000/health
# List loaded templates
curl http://localhost:3000/exec/templates
# Run a command in a new Python session
curl -X POST http://localhost:3000/exec/test-session/execute \
-H "Content-Type: application/json" \
-d '{ "template": "python", "command": "python3", "args": ["--version"] }'All configuration is via environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP server port |
LOG_LEVEL |
debug |
Pino log level |
MCP_AUTH_TOKEN |
(Required) | Bearer token for MCP SSE endpoint |
FIRECRACKER_BIN |
/usr/local/bin/firecracker |
Path to Firecracker binary |
FIRECRACKER_JAILER_BIN |
/usr/local/bin/jailer |
Path to Jailer binary |
FIRECRACKER_JAIL_BASE |
/var/lib/agent-sandbox/jailer |
Base directory for Jailer chroots |
FIRECRACKER_ARTIFACTS_DIR |
/var/lib/agent-sandbox/artifacts |
Snapshot, memory, kernel, and template storage |
FIRECRACKER_UID |
997 |
UID for the Firecracker process |
FIRECRACKER_GID |
982 |
GID for the Firecracker process |
VM_VCPU_COUNT |
1 |
Number of guest vCPUs configured for the VM |
VM_MEM_SIZE_MIB |
128 |
Guest RAM size in MiB (must match the snapshot configuration) |
VM_CPU_QUOTA_US |
50000 |
CPU quota in microseconds for cgroups (bandwidth limit) |
VM_CPU_PERIOD_US |
100000 |
CPU period in microseconds for cgroups |
VM_MEMORY_LIMIT_BYTES |
134217728 (128 MiB) |
Host-side cgroup memory limit in bytes |
VM_NOFILE_LIMIT |
1024 |
Maximum number of open file descriptors for the VM process |
VM_DISK_LIMIT_BYTES |
536870912 (512 MiB) |
Host-side cgroup disk quota limit in bytes |
VM_DNS_MODE |
none |
Per-VM DNS filtering mode (none, allow, deny) |
VM_DNS_DOMAINS |
"" |
Comma-separated domain filter list (e.g. *.npmjs.org,github.com) |
VM_DNS_UPSTREAM |
8.8.8.8,1.1.1.1 |
Comma-separated upstream DNS servers |
VM_DEST_MODE |
none |
Per-VM IP/Port destination filtering mode (none, allow, deny) |
VM_DEST_RULES |
"" |
Destination CIDR/port rules (e.g. 169.254.169.254/32,10.0.0.0/8:443/tcp) |
VM_BW_ENABLED |
false |
Enable TC bandwidth throttling per VM (true, false) |
VM_BW_RATE_KBIT |
10240 |
Rate limit in kbit/s (10240 = 10 Mbit/s) |
VM_BW_BURST_KBIT |
1024 |
Burst limit in kbit |
Built-in Prometheus metrics at /metrics:
| Metric | Type | Description |
|---|---|---|
active_vm_count |
Gauge | Currently running VMs by state |
vm_creation_time |
Histogram | Snapshot restore latency |
exec_sessions_active |
Gauge | Active agent sessions |
exec_session_duration_seconds |
Histogram | Session lifetimes |
exec_message_total |
Counter | Messages by type (execute, write_file, etc.) |
exec_message_duration_seconds |
Histogram | Command round-trip time |
vsock_connection_time |
Histogram | Host ↔ VM connection latency |
vsock_errors_total |
Counter | Connection/parse/timeout errors |
vm_resource_config |
Gauge | Configured host resource limits per VM (labels: resource) |
vm_egress_policy_applied_total |
Counter | Applied VM network egress policies (labels: dns_mode, dest_mode, bw_enabled) |
Additional endpoints:
GET /health- basic liveness checkGET /ready- readiness probe (memory threshold)
# Run all tests
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverageTests cover the session gateway, VM protocol, jailer path handling, cleanup lifecycle, network setup, egress policies, template registry, and MCP tool integration using Vitest and Supertest.
src/
├── server.ts # Entrypoint - HTTP server + host network setup
├── app.ts # Express app - routes, middleware, metrics
├── logger.ts # Structured logging (Pino) with redaction
├── metrics.ts # Prometheus metrics definitions
├── create_snapshot.ts # One-shot script to create VM template snapshots
├── session/
│ ├── session.ts # Session state machine + reaper
│ └── gateway.ts # Lazy VM creation + message dispatch
├── vm/
│ ├── vm-manager.ts # VM lifecycle - create, restore, teardown
│ ├── jailer.ts # Jailer integration - chroot, hardlinks, paths
│ ├── templates.ts # Template registry - discovery, validation & metadata
│ ├── networking.ts # Per-VM network namespace & egress controls
│ ├── egress-policy.ts # Config parser for DNS, IP/Port & TC egress policies
│ ├── egress-policy.test.ts # Unit tests for egress policies
│ ├── protocol.ts # Vsock response parsing + streaming
│ ├── transport.ts # Vsock connection management
│ └── cleanup.ts # Idempotent VM cleanup
├── routes/
│ └── exec.ts # REST API for session execution
└── mcp/
├── server.ts # MCP tool definitions
├── routes.ts # SSE transport + auth middleware
└── stdio.ts # Stdio transport for local MCP
templates/
├── build.sh # Template build pipeline script (Docker -> ext4 -> snapshot)
├── base/
│ └── Dockerfile # Minimal guest base image (socat, node, runtime, start.sh)
├── node/
│ └── Dockerfile # Node.js environment template
├── python/
│ └── Dockerfile # Python 3.12 environment template
└── go/
└── Dockerfile # Go 1.23 environment template
sdk/
├── typescript/ # @agent-sandbox/sdk - TypeScript/JS client (zero deps)
├── src/
│ ├── index.ts # Barrel export
│ ├── client.ts # Sandbox client - session factory + admin
│ ├── session.ts # Session handle - exec, runCode, filesystem
│ └── types.ts # All type definitions
├── package.json
└── README.md
minimal-rootfs/
├── start.sh # Guest init - networking, runtime, socat bridge
└── runtime/
└── runtime.js # Guest-side agent runtime (execute, fs, cancel)
| Component | Technology |
|---|---|
| Execution engine | Firecracker microVMs |
| Process isolation | Jailer (chroot + seccomp + UID separation) |
| Network isolation | Linux network namespaces, veth pairs, TAP, iptables NAT, dnsmasq, tc |
| Host ↔ VM IPC | vsock + socat bridge |
| Agent protocol | Model Context Protocol (MCP) |
| API framework | Express 5 (Node.js) |
| Client SDKs | TypeScript |
| Observability | Pino (structured logs) + prom-client (Prometheus metrics) |
| Testing | Vitest + Supertest |
- Pre-built environment snapshots (Node.js, Python, Go, Custom Dockerfiles)
- Typed client SDKs (TypeScript)
- Per-session resource limits (CPU, memory, disk, network bandwidth)
- Persistent workspace volumes across sessions
- Multi-host execution with session routing
- streaming for real-time output
Vivek Jadhav - github.com/vivek1504