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-transparentagentbox 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 PRnet: 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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentbox_runtime-0.3.0.tar.gz (35.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentbox_runtime-0.3.0-py3-none-any.whl (28.1 kB view details)

Uploaded Python 3

File details

Details for the file agentbox_runtime-0.3.0.tar.gz.

File metadata

  • Download URL: agentbox_runtime-0.3.0.tar.gz
  • Upload date:
  • Size: 35.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agentbox_runtime-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5e7cd2fe96835b7bd850efe6cf2759e17ecbbceca4011b116b5cd75651cba05a
MD5 ab204f560252faf01a091ec3a730d212
BLAKE2b-256 c648c44ccb859ca9622f554968aa743e30f5d6b4ca9273fd6dacd0ca30e4d40f

See more details on using hashes here.

Provenance

The following attestation bundles were made for agentbox_runtime-0.3.0.tar.gz:

Publisher: release.yml on sophie-nguyenthuthuy/agentbox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agentbox_runtime-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agentbox_runtime-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ccaa2f5a2247087b9e9686ba84bdb69f2ef40a10ed8c025cb399ab0cfb313e49
MD5 db729699def874a9fb5be4cf21e003e7
BLAKE2b-256 23d635d90b5e4323c9af92fbcd48a6af8a3ec210fa71bdb088ab32ada78ba163

See more details on using hashes here.

Provenance

The following attestation bundles were made for agentbox_runtime-0.3.0-py3-none-any.whl:

Publisher: release.yml on sophie-nguyenthuthuy/agentbox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page