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:
- Policy — default-deny capability file for filesystem, network, subprocesses, and environment variables.
- Trace — every allowed effect and every denial is appended to a
hash-chained JSONL log. Edit, delete, or reorder one line and
agentbox verifycatches it. - 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 andsys.pathimports 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 anexec:rule, plus deletes/renames outside write roots; - blocks
ctypesdlopen/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.checkpointeffect 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 showshows it). - Fails closed — if the snapshot can't be taken, the mutation is blocked
with
CheckpointErrorrather than proceeding un-undoable. - Replay-transparent —
agentbox replayneither re-snapshots nor diverges on the hook entry. - Drop a
snapback.tomlwithignore = ["trace.jsonl"]next to your agent so rolling back the agent's writes never truncates the audit trail (seeexamples/).
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 onwrite:. - One language runtime per PR — Node (
--requireshim), Deno (permissions bridge), Bun; the policy/trace/replay core is runtime-agnostic. - One trace exporter per PR — OTLP spans, SQLite,
agentbox show --htmltimeline. - 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)
| File | Size | Uploaded | |
|---|---|---|---|
| agentbox_runtime-0.3.0.tar.gz | 35.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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