self-hosted sandbox platform
$ sandfort
Run the code your AI agents write, on hosts you control.
Request beta access → API referenceHosted evaluation beta is open: try the API with zero install on our shared instance. In production, Sandfort runs on your own infrastructure.
the shift
AI agents write code.
Every turn of the loop runs it.
Generated code is only useful once it executes: tests, builds, scripts, dev servers. A coding agent runs code on every turn, and nobody on your team wrote or reviewed it.
the problem
Where does that code run today?
on the agent's own host
One rm -rf or curl | sh away from a bad afternoon.
Full access to keys, files and the network. No limits.
on a shared CI or prod box
Untrusted code lands next to secrets, deploy tokens and every other workload on that machine.
in a hosted sandbox service
Runs isolated, but your code, repositories and data leave your network on every call.
When customers, a security review or a contract say “the code stays here”, none of these work.
the answer
$ The sandbox runs on your side of the line.
integrate
Four ways in. Same sandbox.
The whole agent loop — exec, files, git, PTY, sessions, snapshots, preview URLs — behind one API. Non-zero exits come back as values, so the error goes straight back to the model.
from sandfort_sdk import Client client = Client(base_url=URL, api_key=KEY) sbx = client.create_sandbox(snapshot="python:3.12-slim", wait=True) r = sbx.process_exec(command="echo build; exit 7") # r.exit_code == 7 (returned, not raised) run = sbx.process.code_run("python", "print(21 * 2)") # run.stdout == "42" sbx.fs.upload_file("/work/main.py", source) client.delete(sbx.id)
import { SandfortClient } from "@sandfort/sdk"; const client = new SandfortClient({ baseUrl: URL, apiKey: KEY }); const sbx = await client.createSandbox({ snapshot: "python:3.12-slim" }); const r = await sbx.codeRun("python", "print(21 * 2)"); console.log(r.exitCode, r.stdout); // 0 42 // sessions, async commands, fs, git, snapshots, volumes
$ sandfort login $ sandfort sandbox create demo --snapshot python:3.12-slim --wait $ sandfort exec sbx_… python3 -c "print(21 * 2)" 42 $ sandfort cp ./app sbx_…:/work/app $ sandfort shell sbx_… # interactive PTY
{ "mcpServers": { "sandfort": {
"command": ".venv/bin/python",
"args": ["mcp_server/sandfort_mcp.py"],
"env": { "SANDFORT_BASE_URL": URL, "SANDFORT_API_KEY": KEY }
} } }
# tools any MCP client gets:
# create_sandbox · run_code · run_command · create_snapshot
# upload_text_file · download_text_file · list_sandboxes · delete_sandbox
- REST API: 72 operations in an exported OpenAPI spec
- Python SDK: sync and async clients, sessions, fs, git, PTY, image builder
- TypeScript SDK: the same agent workflow, no runtime dependencies
- MCP server: sandboxes as tools for any MCP client, no SDK integration
- Signed webhooks for lifecycle events, with delivery history and redelivery
- Preview URLs and a web terminal, to show a human what the agent built
isolation
Assume the code is hostile.
$ :(){ :|:& };: # fork bomb
bash: fork: retry: Resource temporarily unavailable
$ curl -m 3 http://169.254.169.254/ # cloud credentials
curl: (7) Failed to connect to 169.254.169.254 port 80
$ env | grep SANDFORT_
(no output)
$ ls /var/run/docker.sock
ls: cannot access '/var/run/docker.sock': No such file
pids_limit=512, 4096 open filesSANDFORT_* names refused at create; server settings never passed incap_drop=ALL (7 added back), no-new-privileges, CPU and memory caps, no swaprunsc) runtime, seccomp profile; prod refuses to start on a weak hostSandboxes are containers that share the host kernel. These controls reduce the risk of an escape; they do not remove it.
network
Reach the API it needs. Nothing else.
POST /v1/sandboxes { "snapshot": "python:3.12-slim", "egress_allowlist": ["example.com"] }
$ curl -sI https://example.com
HTTP/2 200 # CONNECT tunnel through the proxy
$ curl -sI https://other.example.net
curl: (56) CONNECT tunnel failed, response 403
# same proxy, forged credential
$ curl -sI --proxy-user sbx:wrong https://example.com
curl: (56) CONNECT tunnel failed, response 407
- Fail-closed: with no proxy configured, an allowlisted create is refused (409), never run with open egress.
network.block_all: no network at all, for code that needs none.- On Linux hosts the firewall chain admits only the proxy address.
- The cloud metadata address is blocked for every sandbox, always.
guardrails
Built for agents that misbehave.
- Per-org quotas kept in a transactional ledger; only a platform admin can raise them
- Per-org caps: 8 live terminals and 16 running async commands by default
- Rate limits per user and key; JSON request bodies capped at 16 MiB
- Idle sandboxes auto-stop; forgotten ones auto-delete
- Reconciler and GC remove orphaned containers, images and volumes
- Webhook delivery shared fairly between tenants: one slow endpoint cannot stall the rest
An agent stuck in a loop burns its own quota, not your host.
speed
Fast enough for the inner loop.
python examples/benchmark.py on your own hardwareOne local measurement on one host (2026-11-01):
20 creates waiting for started and 50 short commands in one sandbox; macOS
arm64, Docker 29.8.2, alpine:3.20 pre-pulled. Not an SLO and not a comparison
with any other product.
operations
Runs where you already run.
a laptop
- One Python process and a SQLite file
- A local Docker daemon
- Your own instance in under an hour with the install guide
your cluster
- Kubernetes kit with separate
apiandworkerroles, Postgres - Multi-node placement and cross-node routing
- Prometheus
/metrics, OTel tracing, JSON logs with request ids - Read-only web console at
/console - Runbook: backup, restore, upgrade, scaling
Same codebase and the same API from the first experiment to the security review. TLS terminates in front of the API.
identity and access
Your identity provider decides who gets in.
vieweradmin · developer · viewer, mapped to API scopesactive: false removes the membership; audit history staysengineering
Measured, not promised.
Tests: pytest --collect-only,
2026-11-02. Gates: scripts in examples/. CI: green on GitHub as of 2026-11-03.
The audit log is a per-organization hash chain; audit-verify exits non-zero
on any break, so you can put it in cron.
honest limits
What it does not do (yet).
/metrics has no
authentication: terminate TLS in front and keep metrics internal.next step
Looking for 2–3 design partners.
teams building AI coding agents
Your agent needs exec, files, git and a terminal. Your customers need the code to stay on your side.
platform and DevEx teams
You answer to security and compliance, and need auditable, multi-tenant execution in your own cluster.
the pilot
- Free for a limited number of partners; the commercial model is decided after the pilots
- Start on our hosted evaluation beta — zero install, try the API in an afternoon
- Then your own instance, in under an hour with the install guide
- In return: feedback sessions and the right to name you as a reference
- Best-effort support, directly with the maintainer; no SLA
sandfort
$ run untrusted code.
Keep it in-house.
Self-hosted sandboxes for AI-generated code: exec, files, git, terminals and snapshots, with quotas, SSO and an audit chain.