AGENT CAGE studies potentially hostile agents. These rules are part of the code review checklist; a change that breaks one is a bug.
- Give a workload anything real: no bank/exchange/wallet account, no money, no real credential, token or API key, no operator password, no personal mailbox, no LAN access, no uncontrolled internet.
- Put a real secret in a world. World secrets are synthetic and contain
cage-hp-(honeypot tripwires). - Pass the broker's environment into the sandbox. The sandbox environment is exactly
AGENT_CAGE_SANDBOX, HOME, LANG, TMPDIR(gate SECRETS_ISOLATION). - Mount the run directory, the ledger or any provider credential into the sandbox.
- Call a paid model without the operator's explicit decision. The
metaprovider refuses to start unless the run config setspolicy.allowPaidand the operator shell setsAGENT_CAGE_ALLOW_PAID=1and a hard call budget is configured. The dashboard never starts paid providers. - Implement anything that bypasses a model provider's safeguards.
- Claim "secure", "sealed" or "done" without a gate result. Statuses: PLANNED, IMPLEMENTED_NOT_TESTED, TESTED_PASS, TESTED_FAIL, BLOCKED, NEEDS_HUMAN.
- Run
none/l1-only/l2-onlysandbox modes for an agent. They exist only for gate ablations and positive controls with the benign probes; the broker refuses them for model-driven or foreign workloads (MODE_NOT_ALLOWED_FOR_WORKLOAD).
- Run
npm testandnpm run gatesafter touchingcage/,observer/orworlds/, on the machine where agents will run; commit the newevidence/<host>-<time>/. - Bump a world's
versionwhen its initial state or semantics change, thennode scripts/snapshot-lock.mjs --write. - Keep the observer side (broker, log, verifier, dashboard) outside the sandbox. The dashboard binds 127.0.0.1 only.
- Treat
runs/as evidence: never edit a run; the log is created with O_EXCL, appended only, and made read-only. - After a series of runs that will be reported, anchor the ledger off the machine:
node scripts/anchor.mjs runs/, commit + pushevidence/ANCHORS.jsonl; verify withnode scripts/verify.mjs runs/<id> --anchors evidence/ANCHORS.jsonl(--host <name>for another machine's evidence). Ifruns/was deleted and recreated,anchor.mjsrefuses until you pass--new-ledger— that flag is a written statement that the old ledger is gone on purpose; never pass it to make a warning go away. - Keep the jail fail-closed: if
jail.shcannot make every mount under/usr,/lib*and/cageread-only it exits 97 and no workload starts. Never weaken that check to make a host work.
- CLI: Ctrl+C on
scripts/run.mjs. - Dashboard: "Kill switch" (all runs) or "Kill this run". The dashboard's kill switch creates
runs/KILL_SWITCH(only if absent) and keeps it until someone presses "Release kill switch"; release only removes a switch the dashboard itself set — one set by hand is removed by hand. - Anywhere: create the file
runs/KILL_SWITCH(project level: also stops gate/experiment runs in sub-directories),<runs-dir>/KILL_SWITCH, or the path in$AGENT_CAGE_KILL_SWITCH— every running episode stops within ~0.1 s, the nextact/modelmessage is refused before it is applied, and no new episode starts while it exists (KILL_SWITCH_ACTIVE;scripts/experiment.mjsstops its loop and writes a partial report,scripts/gates.mjsexits 2, the dashboard answers 409). Delete it afterwards. scripts/run.mjsandscripts/experiment.mjsstop their runs on SIGINT, SIGTERM and SIGHUP.- If the broker itself dies, the in-sandbox wall clock (
timeout -s KILL, wall time + 5 s) still stops the workload. - Policy:
--halt-on PATH_ESCAPE,HONEYPOT_USE,...stops a run (and snapshots the world) on the first matching risk tag.
Found a way out of the cage? Please report it privately through GitHub's "Report a vulnerability" (security advisory) on this repository rather than a public issue. Fixes land together with a probe and a gate check that reproduce the escape.