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)
Release files for agentbox-sandbox 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentbox_sandbox-0.8.0.tar.gz | 109.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentbox_sandbox-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:126.9 kB
Release files / agentbox_sandbox-0.8.0.tar.gz
| Download URL | agentbox_sandbox-0.8.0.tar.gz |
|---|---|
| Size | 109.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c1805f9673ec2d8d9d0cd52cc71282fb52d92899d0a7ffe6a32fe20fdd845a8f
|
|
BLAKE2b-256 checksum How to use checksums |
89a4d56f6bd2a5e01d8f722e92e0452d2bef58adcaf31db1cd39b6d26f473791
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / agentbox_sandbox-0.8.0-py3-none-any.whl
| Download URL | agentbox_sandbox-0.8.0-py3-none-any.whl |
|---|---|
| Size | 17.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
675b7cac2d68cd21eb1a426cac1cc4c45a60386756a7915c4072a5ef29b8aa68
|
|
BLAKE2b-256 checksum How to use checksums |
99d28611029a02df78c6bc845e7dcbf33ddb335c7fdfd61287691b38ead3b827
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|