Skip to main content

airlock-sandbox

Python SDK for agent-sandbox: run code from AI agents in isolated gVisor containers — no network unless you allow it, hard memory, CPU, process and disk limits, and every outbound connection logged.

pip install airlock-sandbox

No dependencies; Python 3.9+.

Quick start

from airlock_sandbox import Sandbox

with Sandbox(api_key="...", base_url="https://sandbox.example.com") as sbx:
    sbx.files.write("main.py", "print(2 ** 16)")
    result = sbx.run("python3 main.py")
    print(result.stdout)      # 65536
    print(result.exit_code)   # 0

Leaving the with block deletes the session and its files. Settings can come from the environment instead: SANDBOX_API_URL, SANDBOX_API_KEY, and SANDBOX_API_INSECURE=1 for a server with a self-signed certificate (or pass verify="/path/to/ca.pem").

Running commands

result = sbx.run("python3 -m pytest -q", timeout=60)   # 1–60 s
result.stdout, result.stderr, result.exit_code
result.ok            # exit code 0, not timed out, not killed
result.timed_out     # hit the timeout
result.oom_killed    # killed for exceeding the memory limit
result.warnings      # e.g. over the disk quota
result.check()       # raises CommandError unless ok; returns the result
str(result)          # text form with [STDOUT]/[STDERR]/[EXIT CODE] — handy for an LLM

Each command runs in a fresh container; files in the workspace persist between commands, processes do not.

Import a GitHub repository

with Sandbox(egress=["pypi"]) as sbx:
    sbx.import_repo("psf/requests")                     # or a URL; ref="v2.32.3", path="src"
    sbx.run("cd requests && pip install -e . pytest", timeout=60).check()
    print(sbx.run("cd requests && python -m pytest -q tests/test_utils.py", timeout=60).output)

Public repositories only. The server downloads the archive (up to 100 MB) and unpacks it inside the sandbox, within the workspace disk quota, so this works without egress.

Upload, download and snapshots

with Sandbox() as sbx:
    sbx.files.upload("data/input.parquet", open("input.parquet", "rb").read())  # any bytes, up to 50 MB
    sbx.run("python3 analyse.py > out/report.csv").check()
    open("report.csv", "wb").write(sbx.files.download("out/report.csv"))
    open("out.tar.gz", "wb").write(sbx.files.download_archive("out"))           # a folder as .tar.gz

    snap = sbx.snapshot("deps installed")      # outlives the session (kept 30 days on the hosted service)

with Sandbox(snapshot=snap["id"]) as sbx:      # starts with the snapshot's files
    print(sbx.run("ls").stdout)

sbx.restore(id, path=".") unpacks a snapshot into a running sandbox; sbx.list_snapshots() and sbx.delete_snapshot(id) manage them. Snapshots include installed packages under /workspace/.local.

Secrets

with Sandbox(egress=["pypi", "llm"], secrets={"ANTHROPIC_API_KEY": key}) as sbx:
    sbx.run("pip install anthropic", timeout=60).check()
    sbx.run("python my_agent.py", timeout=60)       # sees $ANTHROPIC_API_KEY

Secrets are environment variables for every command. The server never returns them (only sbx.secret_names()), masks them as [secret NAME] if a command prints one, and deletes them with the session. Code in the sandbox can read them, so allow only the hosts it needs: the llm preset covers the major model APIs.

Agent frameworks

Ready-made tools (write_file, read_file, run_command) for the popular frameworks. Install the framework as usual, or with an extra: pip install "airlock-sandbox[langchain]", "[openai-agents]" or "[crewai]".

# LangChain / LangGraph
from langchain.agents import create_agent
from airlock_sandbox.integrations.langchain import airlock_tools

with Sandbox(egress=["pypi"]) as sbx:
    agent = create_agent("anthropic:claude-opus-5-5", tools=airlock_tools(sbx))
    agent.invoke({"messages": [{"role": "user", "content": "Write fizzbuzz.py and run it"}]})
# OpenAI Agents SDK
from agents import Agent, Runner
from airlock_sandbox.integrations.openai_agents import airlock_tools

with Sandbox() as sbx:
    agent = Agent(name="coder", instructions="Write code and run it.", tools=airlock_tools(sbx))
    print(Runner.run_sync(agent, "Write fizzbuzz.py and run it").final_output)
# CrewAI
from crewai import Agent, Crew, Task
from airlock_sandbox.integrations.crewai import airlock_tools

with Sandbox() as sbx:
    coder = Agent(role="Developer", goal="Write working code", backstory="A careful engineer.",
                  tools=airlock_tools(sbx))
    task = Task(description="Write fizzbuzz.py and run it", expected_output="Its output", agent=coder)
    print(Crew(agents=[coder], tasks=[task]).kickoff())

airlock_tools(sbx, tools=["run_command"]) picks a subset. Tool errors come back to the model as text it can act on, and long output is truncated in the middle.

Long-running sandboxes and previews

with Sandbox(persistent=True, egress=["pypi"]) as sbx:
    sbx.files.write("app.py", "from flask import Flask\napp = Flask(__name__)\n@app.get('/')\ndef hi(): return 'hi'")
    sbx.run("pip install flask", timeout=60).check()
    sbx.run("nohup flask --app app run --host 0.0.0.0 --port 8000 > flask.log 2>&1 &").check()
    print(sbx.expose(8000))        # https://8000-<token>.<preview domain>, open while the sandbox is

One container runs for the whole session, so background processes keep running between commands (redirect their output, as above). Each account can run a few at a time. The server's interactive terminal is a WebSocket; see the API reference.

Internet access (egress)

Sandboxes have no network by default. Ask for specific hosts — they must be allowed by your tenant's policy on the server:

with Sandbox(egress=["pypi"]) as sbx:              # presets: pypi, npm, github, huggingface, llm
    sbx.run("pip install requests", timeout=60).check()
    for event in sbx.egress_log():
        print(event["decision"], event["host"], event.get("reason", ""))

Only HTTPS to the listed hosts works; everything else is refused and logged. sbx.egress_policy() lists what your tenant may request.

Use as tools for an LLM agent

from airlock_sandbox import Sandbox
from airlock_sandbox.tools import openai_tools, anthropic_tools, handle_tool_call

# OpenAI
response = client.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=openai_tools())
for call in response.choices[0].message.tool_calls or []:
    output = handle_tool_call(sbx, call.function.name, call.function.arguments)

# Anthropic
response = client.messages.create(model="claude-sonnet-5", max_tokens=2048, messages=messages, tools=anthropic_tools())
for block in response.content:
    if block.type == "tool_use":
        output = handle_tool_call(sbx, block.name, block.input)

Tools: write_file, read_file, run_command. handle_tool_call never raises — errors come back as text the model can act on — and long output is trimmed to 8,000 characters, keeping the beginning and the end.

MCP server (Claude Code, Claude Desktop, other MCP clients)

Remote (recommended): nothing to install. The server has an MCP endpoint at /mcp; connect with a URL and your key:

claude mcp add --transport http --scope user airlock "https://<server>/mcp?egress=pypi" --header "X-API-Key: <key>"
  • ?egress=pypi (comma-separated hosts or presets) gives the sandbox internet access, within your tenant's policy; leave it out for no network.
  • There's one sandbox per key, reused by every call and reconnect; it closes after 30 minutes idle. Add &workspace=<name> for a separate sandbox.
  • Tools: run_command, write_file, read_file, list_files, import_repo, egress_log, activity, save_snapshot, list_snapshots, restore_snapshot, sandbox_info, reset_sandbox. Commands show up in the console's Activity page as mcp key <id>.

Local: the package also includes an MCP server that runs on your machine over stdio and talks to the API. Install with the mcp extra (Python 3.10+):

pip install "airlock-sandbox[mcp]"

Claude Code:

claude mcp add airlock --scope user -e SANDBOX_API_URL=https://<server> -e SANDBOX_API_KEY=<key> -e AIRLOCK_EGRESS=pypi -- airlock-sandbox-mcp

Add -e SANDBOX_API_INSECURE=1 if the server uses a self-signed certificate. With uv you can skip the install and use -- uvx --from "airlock-sandbox[mcp]" airlock-sandbox-mcp as the command instead. Put the server name (airlock) right after add: -e takes several values and would otherwise swallow the name. If uvx reports an old version, run it once with --refresh.

If Claude Code says the server timed out: it waits 30 seconds for a server to start. On Windows, importing the mcp library alone can take 8–10 seconds (antivirus scanning its many files), and uvx adds an install on the first start after each release. Two fixes, either works:

  • Install the tool once, so starting it never installs anything, and point Claude Code at it:

    uv tool install "airlock-sandbox[mcp]"     # later: uv tool upgrade airlock-sandbox
    claude mcp add airlock --scope user -e SANDBOX_API_URL=https://<server> -e SANDBOX_API_KEY=<key> -e AIRLOCK_EGRESS=pypi -- airlock-sandbox-mcp
    
  • Give servers longer to start, by setting MCP_TIMEOUT (milliseconds) before starting Claude Code, e.g. MCP_TIMEOUT=60000 claude (PowerShell: $env:MCP_TIMEOUT=60000; claude).

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "airlock": {
      "command": "airlock-sandbox-mcp",
      "env": {
        "SANDBOX_API_URL": "https://<server>",
        "SANDBOX_API_KEY": "<key>",
        "AIRLOCK_EGRESS": "pypi"
      }
    }
  }
}

On Windows, if Claude Desktop can't find the command, use its full path (where airlock-sandbox-mcp).

Tools: run_command, write_file, read_file, list_files, import_repo, egress_log, activity, save_snapshot, list_snapshots, restore_snapshot, sandbox_info, reset_sandbox. Each MCP server process gets one sandbox session, created on first use and deleted when the client disconnects; files persist between commands within it. AIRLOCK_EGRESS (or --egress) sets which hosts the sandbox may reach — it must be allowed by your tenant's policy; leave it unset for no network.

Errors

All errors inherit from SandboxError:

Exception When
AuthenticationError Missing or invalid API key (401)
PermissionDeniedError Path outside the workspace, egress outside your policy, someone else's session (403)
NotFoundError Session doesn't exist or expired (404)
QuotaExceededError Write would exceed the workspace disk quota (413)
RateLimitError Tenant limit hit: sessions, requests/minute or concurrent commands (429); see .retry_after
CapacityError Every workspace on the server is in use (503)
ValidationError Invalid request, e.g. unknown template (400/422)
APIConnectionError Server unreachable
CommandError Raised by CommandResult.check()

Rate limits (429) and a full server (503) are retried automatically, honouring the server's Retry-After (max_retries=3 by default).

API keys

Create, list and revoke keys without your operator's help — for example to rotate:

from airlock_sandbox.keys import Keys

keys = Keys()                          # uses SANDBOX_API_URL / SANDBOX_API_KEY
new = keys.create(name="laptop-2026")  # new["api_key"] is shown only once — store it
keys.revoke("<old key_id>")            # stops working immediately
keys.list()

Or from the shell: airlock-sandbox-keys create --name ci, airlock-sandbox-keys list, airlock-sandbox-keys revoke <key_id>. Operators use --admin / Keys(admin=True) with SANDBOX_ADMIN_KEY to issue keys for any tenant.

Account

sbx.usage()    # {"limits": {...}, "sessions_open": 1, "commands_running": 0, "requests_available": 118}
sbx.health()

page = sbx.audit(limit=50)          # this tenant's command history, newest first (never output)
for e in page["entries"]:
    print(e["exit_code"], e["command"], e["actor"])
older = sbx.audit(before=page["next"]) if page["next"] else None

License

MIT © 2026 Farvez Anzam. The Airlock server it talks to is AGPL-3.0; see the main repository.

Metadata

Release files for airlock-sandbox 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for airlock-sandbox 0.9.0
File Size Uploaded
airlock_sandbox-0.9.0.tar.gz 31.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for airlock-sandbox 0.9.0
File Interpreter ABI Platform
airlock_sandbox-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 62.9 kB

Release files / airlock_sandbox-0.9.0.tar.gz

Download URL airlock_sandbox-0.9.0.tar.gz
Size 31.9 kB
Tags Source
SHA-256 checksum
How to use checksums
44e1b8833dad4df8a60551cc9a96806ea9dcdb5ef5b3a5f058182452b38cdd3f
BLAKE2b-256 checksum
How to use checksums
83e623a4de1b28e44d2823a13a3eaf1dccb15641258541f6655001f6de7363d4
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 Oct 11, 2026.

Transparency log

Release files / airlock_sandbox-0.9.0-py3-none-any.whl

Download URL airlock_sandbox-0.9.0-py3-none-any.whl
Size 31.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
550a54fafdbfb2ac57e907a90fdb07f738532ed0ce00adfed469cd58bc69e7d3
BLAKE2b-256 checksum
How to use checksums
83b41d05292621f0320653614c6d57d6a2045df4e4dd6290a3dded262a7d3910
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 Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page