This release is a pre-release and may not be stable for production use.
sandboxio
One secure Python API for running AI-agent code in any sandbox. Swap Docker ↔ E2B with one line; no network by default; test your agent tools offline with the built-in fake.
Why this exists
Every sandbox provider ships its own SDK with its own shape, so agent code that runs on one is rewritten for the next. The framework layers each solve it captively — LangChain's backends only help inside LangChain. sandboxio is the framework-agnostic substrate underneath, with the security posture that agent execution actually needs.
- Security is the headline, not an add-on. Deny-by-default egress, mandatory timeouts, isolation-tier reporting and audit hooks are all in v0.1 — not a later hardening pass.
- No lowest common denominator. One-backend features stay reachable through
Capabilityflags and.nativeinstead of being sanded off. - Tiny, auditable core.
anyioandtyping-extensionsonly; every backend SDK sits behind an extra and is imported lazily. - Offline-testable.
FakeBackendand a pytest fixture, so your agent's tools have tests that need no Docker, no network and no provider account. - Usable without writing Python. The same sandbox and the same defaults are served over MCP, so a client like Claude Desktop or Cursor gets code execution it cannot reconfigure at runtime.
- Provider churn is absorbed publicly. Upstream breaking changes are tracked, absorbed and written down.
Quickstart
Every Python block below is executed by CI exactly as written
(tests/test_readme_examples.py); a copied example that
does not run is a P0 bug. The Docker-backed lines run against the built-in fake in CI, so
they are the same code you would run — only the backend differs.
The commands below are what v0.1 will install. Today the name on PyPI holds a 0.0.0
placeholder that carries no code, so uv add sandboxio does not yet get you a library.
uvx sandboxio demo is the documented entry point — never the sbx alias, which resolves
to an unrelated PyPI package (ADR-0014).
uv add "sandboxio[docker]" # from v0.1
uvx sandboxio demo # create, run, stream, prove egress is denied, tear down
sandboxio doctor # what is installed, reachable and missing — no secrets printed
Run code in a sandbox. create() with no arguments is local Docker; nothing leaves the
sandbox unless you say so.
import sandboxio
async with await sandboxio.create() as sb: # zero-config, local Docker
res = await sb.run_code("print('hello')")
print(res.stdout) # hello
res = await sb.run(["python", "--version"], timeout=30)
print(res.exit_code, res.stdout.strip())
Swap the backend with one line. The code that uses the sandbox does not change.
import sandboxio
async def summarise(sb: sandboxio.protocols.AsyncSandbox) -> str:
await sb.files.write("/work/input.txt", "3 4\n")
res = await sb.run_code("a, b = open('/work/input.txt').read().split(); print(int(a) * int(b))")
res.raise_for_status() # ExecutionError on a non-zero exit
return res.stdout.strip()
async with await sandboxio.create("docker://python:3.12-slim") as sb:
print(await summarise(sb))
async with await sandboxio.create("e2b://code-interpreter-v1") as sb: # needs E2B_API_KEY
print(await summarise(sb))
Stream output while a long command runs, and rely on the timeout: a sandbox timeout is
sandboxio.SandboxTimeout, never the builtin TimeoutError.
import sandboxio
async with await sandboxio.create() as sb:
async with sb.stream(["sh", "-c", "echo start; sleep 1; echo done"], timeout=30) as proc:
async for chunk in proc:
print(chunk.stream, chunk.data.decode(), end="")
res = await proc.wait()
try:
await sb.run(["sleep", "999"], timeout=1)
except sandboxio.SandboxTimeout as exc:
print(exc.code) # SBX_E1302 — stable, documented
Sync code gets the same surface through create_sync(). One sandbox is one portal thread;
use the async API for heavy concurrency.
import sandboxio
with sandboxio.create_sync("docker://python:3.12-slim", timeout=60) as sb:
print(sb.run_code("print(6 * 7)").stdout)
Test your agent's tools offline. sbx_fake is a pytest fixture installed with the package;
it executes nothing, records everything, and passes the same contract suite as the real
backends.
import pytest
from sandboxio import ExecResult
@pytest.mark.anyio
async def test_my_tool_reads_the_pandas_version(sbx_fake):
sbx_fake.on_run_code(match="import pandas", returns=ExecResult(0, "2.2.1\n", ""))
res = await sbx_fake.sandbox.run_code("import pandas; print(pandas.__version__)")
assert "2.2.1" in res.stdout
assert sbx_fake.calls[0].network.egress == "deny" # the default, recorded
Full surface, including capability discovery, require_isolation, audit sinks and spans:
docs/quickstart.md and the normative
docs/spec/03-public-api.md.
Backends
| Backend | Status | Isolation tier |
|---|---|---|
| Docker | adapter built, unreleased | CONTAINER — containers share the host kernel |
| E2B | adapter built, unreleased | MICROVM — Firecracker microVM, its own kernel |
FakeBackend |
built, unreleased | n/a — in-process, for tests |
| Modal | planned for v0.1.1 | GVISOR — containerised and virtualised using gVisor |
Every tier above is the mechanism the provider documents for itself, read on 2026-09-20.
None of those three pages carries its own revision date, so that is the date it was read, not
a date the provider published; re-verification is a quarterly item.
What the tiers mean — and why GVISOR is defence in depth rather than VM equivalence — is in
docs/explanation/isolation-tiers.md.
Third-party adapters are first-class: the adapter contract is specified and enforced by a shared contract suite.
Integrations
- LangGraph / LangChain —
sandboxio.integrations.langgraph.make_code_tool()returns a nativeBaseTool(sandboxio[langgraph]). - OpenAI Agents SDK —
sandboxio.integrations.openai_agents.make_code_tool()returns a nativeFunctionTool(sandboxio[openai-agents]). - MCP —
python -m sandboxio.mcp --backend docker://python:3.12-slimservesrun_python,run_command, file tools andsandbox_info(sandboxio[mcp]), also as a container image.
Documentation
docs.sandboxio.dev — the Markdown under
docs/ is its source and stays the source, so nothing below moves. Read it here
or there:
examples/— complete programs, one concept each: hello world, swapping backends, streaming, proving egress is denied, agent tools. Each one is executed by CI.docs/quickstart.md— five minutes from install to a sandboxed run.docs/how-to/— Docker images and offline wheelhouses, E2B keys and allowlists, offline testing with the fake, audit sinks and tracing, CI, and troubleshooting by symptom.docs/explanation/— the security model, isolation tiers, deny-by-default, why errors have codes, the version policy and why sandboxio and not something else.docs/faq.md— the questions a newcomer asks first, answered short.docs/errors/— every error code, generated from the source.docs/spec/— the normative specification. This is the contract.docs/adr/— why each decision was made, including the ones that look arbitrary.docs/llms.txt— for coding assistants;llms-full.txtbeside it.
Security
Isolation tiers are reported, not assumed: CONTAINER is not a security boundary against
hostile code, and sandboxio says so rather than implying otherwise.
Report vulnerabilities privately — see SECURITY.md. A report that a
declared control did not apply is the highest-severity class this project has.
The project's own supply-chain posture is scored weekly by OpenSSF Scorecard and published — the badge above links to the run that produced it.
Contributing
Read CONTRIBUTING.md. Contributions need a DCO Signed-off-by line;
there is no CLA.
License
Code is MIT. Prose in docs/ is CC BY 4.0; code samples inside
those documents are MIT (ADR-0026).
Metadata
Release files for sandboxio 0.1.0rc4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sandboxio-0.1.0rc4.tar.gz | 336.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sandboxio-0.1.0rc4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 403.1 kB
Release files / sandboxio-0.1.0rc4.tar.gz
| Download URL | sandboxio-0.1.0rc4.tar.gz |
|---|---|
| Size | 336.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
65f8c1bfc93d6caa2dc22245b07d68da5110cb071144556294ba4c6cec383736
|
|
BLAKE2b-256 checksum How to use checksums |
721c5479348aec5c37f6a4db244b463ce3acfe7ade94a7a49d27b77a321c6955
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / sandboxio-0.1.0rc4-py3-none-any.whl
| Download URL | sandboxio-0.1.0rc4-py3-none-any.whl |
|---|---|
| Size | 67.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e688e5324b9adf6ff94404be59532ca6b85c4abf588525143a531d09c58aebc9
|
|
BLAKE2b-256 checksum How to use checksums |
aaf0fc5ba08c050bf6830ea8345e8449cdb6e828203729b830fb6b81c6345173
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log