Skip to main content

agentbox

A sandbox runtime for AI agent processes. One policy file says what the agent may touch; agentbox wraps the process, enforces the policy at the interpreter level, captures a tamper-evident trace of every side effect, and can deterministically replay any recorded run.

allow: read ./src, net: api.github.com

Zero dependencies. Pure Python stdlib. Python 3.11+.

Why

Agents run with your whole user account: your SSH keys, your ~/.aws, your network. Reviewing what an agent did after the fact means scrolling logs that the agent itself could have written. agentbox gives you three properties that compose:

  1. Policy — default-deny capability file for filesystem, network, subprocesses, and environment variables.
  2. Trace — every allowed effect and every denial is appended to a hash-chained JSONL log. Edit, delete, or reorder one line and agentbox verify catches it.
  3. Replay — re-run the same command against the trace: recorded effects are served back (network never touched, clock and RNG frozen, writes skipped), and any behavioral divergence — including an input file whose content changed — fails the replay with the exact step that differed.

Quickstart

pip install agentbox-runtime

agentbox init                                   # writes a starter agentbox.policy
agentbox run    -p agentbox.policy -- python agent.py    # record
agentbox show                                   # inspect the trace
agentbox verify                                 # check the hash chain
agentbox replay -p agentbox.policy -- python agent.py    # deterministic re-run

A run looks like this:

$ agentbox run -p agentbox.policy -- python demo_agent.py
wrote out/summary.txt at 1786243519.9181929
blocked as expected: agentbox: policy denies read /etc/passwd
agentbox: recorded 4 effects, 1 observations, 1 denials -> trace.jsonl

$ agentbox replay -p agentbox.policy -- python demo_agent.py
wrote out/summary.txt at 1786243519.9181929        # same clock value, replayed
agentbox: replay ok — 5/5 recorded steps matched

Policy file

One rule per line, # comments, everything not allowed is denied:

read:  ./data              # file reads under this root
write: ./out               # writes (implies read) under this root
net:   api.github.com      # DNS + connect to this host
net:   *.githubusercontent.com
net:   127.0.0.1:8080      # optional port pin
net:   unix:/tmp/app.sock  # unix sockets
exec:  git status          # argv prefix match
exec:  echo                # bare program name: any args
env:   HOME                # env vars passed into the sandbox
env:   AWS_*               # globs work everywhere

allow: read ./src is an alias for read: ./src, and rules can share a line: allow: read ./src, net: api.github.com.

How enforcement works

agentbox run spawns your command with a scrubbed environment (only env:-allowed variables survive) and injects a sitecustomize shim via PYTHONPATH. Before any user code runs, the shim installs a sys.addaudithook guard — CPython audit hooks cannot be removed once installed, so the agent's own code can't disarm it. The guard:

  • blocks open() outside the policy roots (stdlib and sys.path imports are exempt), and records allowed reads with a content sha256;
  • blocks DNS resolution and socket connects to non-allowed hosts;
  • blocks subprocess/os.system/os.exec* spawns that don't match an exec: rule, plus deletes/renames outside write roots;
  • blocks ctypes dlopen/dlsym — the classic audit-hook bypass;
  • fails closed: if the sandbox can't arm, the process refuses to start.

For replayable effects, agents use the SDK (anything outside it is still guarded and observed):

import agentbox.client as box

text = box.read_text("data/notes.txt")
resp = box.get("https://api.github.com/repos/x/y")   # {"status", "body", "sha256"}
out  = box.run(["git", "status"])                    # {"code", "stdout", "stderr"}
t    = box.now()      # frozen on replay
r    = box.random()   # frozen on replay
box.write_text("out/report.md", text.upper())

Undo what the agent did

Policy says what the agent may touch — but allowed ≠ wanted. With snapback installed, --checkpoint snapshots the working tree right before the agent's first mutating effect, so every recorded run is one command away from never having happened:

pip install snapback-cli

$ agentbox run --checkpoint -p agentbox.policy -- python agent.py
agent done: rewrote data/notes.txt, wrote out/report.md
agentbox: recorded 4 effects, 0 observations, 0 denials -> trace.jsonl
agentbox: checkpoint 20260809-143103 taken before first mutation — `snapback undo` reverts this run

$ snapback diff              # what did it actually change?
A out/report.md
M data/notes.txt

$ snapback undo              # put it all back
snapback: restored 20260809-143103

Properties worth knowing:

  • Lazy — the snapshot is taken on the first write_text/run, not at startup. Runs that only read cost nothing.
  • In the chain — the checkpoint is recorded as a hook.checkpoint effect in the same hash-chained trace, so "what can I roll back to" is part of the same tamper-evident record as "what did it do" (agentbox show shows it).
  • Fails closed — if the snapshot can't be taken, the mutation is blocked with CheckpointError rather than proceeding un-undoable.
  • Replay-transparent — agentbox replay neither re-snapshots nor diverges on the hook entry.
  • Drop a snapback.toml with ignore = ["trace.jsonl"] next to your agent so rolling back the agent's writes never truncates the audit trail (see examples/).

Full demo: examples/checkpoint_agent.py.

Node agents

The same runner sandboxes Node processes — no extra install:

agentbox run    -p agentbox.policy -- node agent.js
agentbox replay -p agentbox.policy -- node agent.js

A CommonJS shim is injected via NODE_OPTIONS --require; it enforces the same policy file (fs, net, child_process, env scrub), appends to the same hash chain the runner starts, and exposes the SDK as globalThis.agentbox:

const box = globalThis.agentbox;
const text  = box.readText("data/notes.txt");
const resp  = await box.get("https://api.github.com/repos/x/y");
const out   = box.run(["git", "status"]);
const t     = box.now();      // integer ms, frozen on replay
const r     = box.random();   // integer in [0, 2^48), frozen on replay
box.writeText("out/report.md", text.toUpperCase());

Caveat: the Node guard monkeypatches fs/net/child_process — it contains well-behaved agents and prompt-injected tool calls, but unlike CPython's irremovable audit hooks it is not a boundary against code that deliberately unpatches it. Hardening it (Node permission model flags) is a welcome PR.

Threat model, honestly

agentbox is an interpreter-level sandbox for Python processes, not a kernel one. What that means in practice:

Stops Doesn't stop
An agent (or its prompt-injected tool call) reading ~/.ssh, posting to an unapproved host, spawning rm -rf A malicious C extension making raw syscalls
Secret env vars leaking into the process at all Bugs in CPython itself
Post-hoc log tampering (hash chain) A hostile human with local root
Silent behavioral drift between runs (replay divergence) Non-Python child processes (their spawn is policy-checked; their own IO is not yet guarded)

Kernel backends (Landlock on Linux, Seatbelt on macOS) are on the roadmap as defense-in-depth; the policy file and trace format won't change.

Replay semantics

  • SDK effects are matched in order by (op, args) and served from the trace.
  • Guard-observed direct IO is verified: a read whose file content changed since recording is a divergence, with the sha256 diff in the report.
  • Live network and subprocess spawns are blocked during replay — replayable effects must go through the SDK.
  • Replay from the same working directory; relative paths are part of the match.

Roadmap — contributions are deliberately bite-sized

Each of these is a single focused PR:

  • One policy rule per PR — net: cidr/…, rate limits (net: api.x.com @10/min), read-once:, size caps on write:.
  • One language runtime per PR — Node (--require shim), Deno (permissions bridge), Bun; the policy/trace/replay core is runtime-agnostic.
  • One trace exporter per PR — OTLP spans, SQLite, agentbox show --html timeline.
  • Kernel backends: Landlock, Seatbelt, seccomp-bpf.
  • Adapters: LangGraph / OpenAI Agents / Claude Agent SDK tool-call wrappers.

See CONTRIBUTING.md.

Development

python -m pytest        # 59 tests, all stdlib + pytest (Node tests auto-skip without node)

MIT © Sophie Nguyen Thu Thuy

Metadata

Release files for agentbox-runtime 0.3.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 agentbox-runtime 0.3.0
File Size Uploaded
agentbox_runtime-0.3.0.tar.gz 35.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agentbox-runtime 0.3.0
File Interpreter ABI Platform
agentbox_runtime-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 63.3 kB

Release files / agentbox_runtime-0.3.0.tar.gz

Download URL agentbox_runtime-0.3.0.tar.gz
Size 35.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5e7cd2fe96835b7bd850efe6cf2759e17ecbbceca4011b116b5cd75651cba05a
BLAKE2b-256 checksum
How to use checksums
c648c44ccb859ca9622f554968aa743e30f5d6b4ca9273fd6dacd0ca30e4d40f
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 Aug 9, 2026.

Transparency log

Release files / agentbox_runtime-0.3.0-py3-none-any.whl

Download URL agentbox_runtime-0.3.0-py3-none-any.whl
Size 28.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ccaa2f5a2247087b9e9686ba84bdb69f2ef40a10ed8c025cb399ab0cfb313e49
BLAKE2b-256 checksum
How to use checksums
23d635d90b5e4323c9af92fbcd48a6af8a3ec210fa71bdb088ab32ada78ba163
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 Aug 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

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