agentbox
Self-hosted code execution sandbox for AI agents — one docker compose up gives you an HTTP API for running untrusted code in isolated environments.
Status: v0.8 — Docker/subprocess sandboxes, egress allowlists, timeouts, memory limits, snapshots, TypeScript client.
60-second try
pip install agentbox-sandbox
agentbox serve # API on :8080
# or: docker compose up agentbox
curl -s http://localhost:8080/health
curl -s -X POST http://localhost:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"code":"print(sum(range(10)))"}'
Why this vs alternatives
| Approach | Strength | Gap |
|---|---|---|
| agentbox | Self-hosted HTTP API + SDK; Docker or subprocess backends | Not gVisor/Firecracker (yet) |
| Hosted sandboxes (E2B, etc.) | Strong isolation, managed | Per-second cost; data leaves your network |
Raw docker exec |
Familiar | No agent-oriented API / snapshots / limits |
| YOLO in the agent process | Zero infra | Full host compromise risk |
Problem
Every agent that writes and runs code needs a safe execution environment. Teams either YOLO in shared containers or pay per-second for hosted sandboxes. Self-hosting gVisor/Firecracker is weeks of work.
Key features (v0.8)
- HTTP API:
POST /v1/runexecutes Python or JavaScript - Docker backend (
AGENTBOX_SANDBOX_BACKEND=docker): ephemeraldocker run --rm,--network=none, optional--memory, workspace at/work - Subprocess backend remains the default for easy local tests
- Per-request
limits.timeout_seconds(HTTP 408 on timeout) - Per-request
limits.memory_mb(Docker--memoryor subprocessRLIMIT_AS); response includeslimits_applied+oom_killed AGENTBOX_MAX_MEMORY_MBclamps requested memory- Default-deny egress: Docker
--network=none/ Linuxunshare/bwrap(network_isolated) - Egress allowlists:
AGENTBOX_EGRESS_ALLOWLIST+ per-requestegress_allowlist(soft userspace filter when non-empty) - Workspace snapshots:
"snapshot": truethen"snapshot_id" - Python SDK + TypeScript client (
sdk/ts/client.ts) - Credential stripping when the backend is not
unrestricted
Architecture
Agent / SDK
└── POST /v1/run
├── SubprocessSandbox (default — easy tests)
└── DockerSandbox (recommended — stronger isolation)
| Component | Technology | Why |
|---|---|---|
| API | FastAPI | Async-ready, OpenAPI docs, widely adopted |
| Server | uvicorn | Standard ASGI server |
| Config | pydantic-settings | Typed env config |
| Isolation | Docker / subprocess | Docker for production; subprocess for CI/dev |
| Tests | pytest + httpx TestClient | Fast API testing |
Installation
pip install agentbox-sandbox
pip install -e ".[dev]"
Usage
Start server
agentbox serve
# or
docker compose up agentbox
Recommended: Docker sandbox backend
Requires the Docker CLI (and a reachable daemon) on the host running agentbox:
export AGENTBOX_SANDBOX_BACKEND=docker
# optional:
# export AGENTBOX_DOCKER_IMAGE=python:3.12-slim
# export AGENTBOX_DOCKER_NODE_IMAGE=node:20-slim
agentbox serve
Each /v1/run starts an ephemeral container, mounts a temp workspace at /work, applies timeout on docker run, and removes the container (--rm). Health and run responses report backend: "docker".
Run code
curl -X POST http://localhost:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"code": "print(sum(range(10)))"}'
curl -X POST http://localhost:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"code": "x = bytearray(10**9)", "limits": {"memory_mb": 64, "timeout_seconds": 5}}'
# Allowlisted egress (subset of AGENTBOX_EGRESS_ALLOWLIST when set):
curl -X POST http://localhost:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"code":"print(1)","egress_allowlist":["example.com"]}'
Python SDK
from agentbox.sdk.client import AgentboxClient
client = AgentboxClient("http://localhost:8080")
print(client.health())
print(client.run("print('hello')"))
print(client.run("console.log('hello')", language="javascript", timeout_seconds=5))
print(client.run("x = bytearray(10**8)", memory_mb=64))
snap = client.run("open('memo.txt','w').write('kept')", snapshot=True)
print(client.run("print(open('memo.txt').read())", snapshot_id=snap["snapshot_id"]))
client.close()
Docker
docker compose up agentbox # start API on :8080 (subprocess backend)
docker compose run --rm test # unit tests
Compose + Docker sandbox backend
export AGENTBOX_HOST_TMP="$(pwd)/.agentbox-work"
mkdir -p "$AGENTBOX_HOST_TMP"
docker compose -f compose.yaml -f compose.docker-sandbox.yaml up --build agentbox
Uses image target runtime-docker (docker-cli + AGENTBOX_SANDBOX_BACKEND=docker), mounts the Docker socket, and bind-mounts AGENTBOX_HOST_TMP at the same absolute path so nested docker run -v works on Docker Desktop.
Configuration
| Variable | Default | Description |
|---|---|---|
AGENTBOX_HOST |
0.0.0.0 |
Bind host |
AGENTBOX_PORT |
8080 |
Bind port |
AGENTBOX_DEFAULT_TIMEOUT_SECONDS |
30 |
Execution timeout |
AGENTBOX_DEFAULT_MEMORY_MB |
unset | Optional default memory cap |
AGENTBOX_MAX_MEMORY_MB |
8192 |
Clamp requested memory_mb to this max |
AGENTBOX_SANDBOX_BACKEND |
subprocess |
subprocess | docker | unrestricted |
AGENTBOX_DOCKER_IMAGE |
python:3.12-slim |
Image for Python runs (docker backend) |
AGENTBOX_DOCKER_NODE_IMAGE |
node:20-slim |
Image for JavaScript runs (docker backend) |
AGENTBOX_EGRESS_ALLOWLIST |
empty | Comma-separated host[:port]; empty = deny-all |
AGENTBOX_SNAPSHOT_DIR |
/tmp/agentbox-snapshots |
Workspace snapshot store |
Running tests
pytest tests/ -v
# Docker unit tests mock the CLI; live Docker run is skipped if docker is unavailable
pytest tests/test_docker.py -v
Roadmap
- Node.js runtime + TypeScript client + per-run timeout
- Filesystem snapshot/restore (tar workspaces)
-
limits.memory_mbviaRLIMIT_AS/ Docker--memory - Default-deny egress via Linux netns (
unshare/bwrap) when available - Docker ephemeral-container backend
- Egress allowlists (
AGENTBOX_EGRESS_ALLOWLIST+ per-request) - gVisor runsc backend with warm pool
- Kernel/iptables egress enforcement (beyond soft userspace hooks)
License
MIT
Known limitations (v0.8)
- Subprocess default is convenient for tests — not production-grade isolation; use
AGENTBOX_SANDBOX_BACKEND=dockerfor stronger isolation - Docker backend needs a local Docker CLI/daemon; missing CLI returns a clear 400
- Subprocess
RLIMIT_ASis a soft address-space cap, not a cgroup memory controller (Docker uses--memory) - Subprocess default-deny egress uses Linux
unshare/bwrapwhen present; macOS stays credential-scrub only (network_isolated: false) unless an allowlist soft-hook applies - Allowlists are a soft Python/Node userspace filter (not iptables); determined code can bypass
- Single-node, no warm pool
- TypeScript client is source-only (not published to npm)
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentbox_sandbox-0.8.0.tar.gz.
File metadata
- Download URL: agentbox_sandbox-0.8.0.tar.gz
- Upload date:
- Size: 109.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1805f9673ec2d8d9d0cd52cc71282fb52d92899d0a7ffe6a32fe20fdd845a8f
|
|
| MD5 |
011c0cc344ac6d0036f1b0b542e31605
|
|
| BLAKE2b-256 |
89a4d56f6bd2a5e01d8f722e92e0452d2bef58adcaf31db1cd39b6d26f473791
|
File details
Details for the file agentbox_sandbox-0.8.0-py3-none-any.whl.
File metadata
- Download URL: agentbox_sandbox-0.8.0-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
675b7cac2d68cd21eb1a426cac1cc4c45a60386756a7915c4072a5ef29b8aa68
|
|
| MD5 |
dcc7a172a45dc7f44afe5ea9b820dbe5
|
|
| BLAKE2b-256 |
99d28611029a02df78c6bc845e7dcbf33ddb335c7fdfd61287691b38ead3b827
|