Cage docs: rules for commands, sites, files and tool servers
Cage is the firewall in the Context Mode gateway. It reads each tool call from Claude Code and Codex before the client runs it, and answers a blocked call with a refusal. This page lists every feature and every limit. For the short version, see Cage.
Key facts
- What it is
- The tool-call firewall in Context Mode Gateway. It checks shell commands, file reads and writes, web fetches and tool-server calls by what they do, across 22 capabilities.
- Who it is for
- Developers and security leads who let Claude Code or Codex run commands.
- Clients
- Claude Code and Codex are enforced, including each call inside a Codex exec cell. Other agents are recorded, not enforced. Part of Pro.
- Measured result
- No block-rate study yet. A command hidden behind one of 98 wrapper words or 33 carriers meets the same rule as the bare command.
- Limits
- Cage reads the call, not the program: a script file, a compiled program or a path built at run time gets past it. Keep your agent's sandbox on.
Cage is in every plan and is off until you turn it on. Nothing is blocked until you pick a preset or add rules. Balanced and Locked down include Core protections.
Overview
Cage is off until you turn it on. Turning it on alone blocks nothing, and no preset is picked for you: you choose what it blocks.
- Core protections: part of Balanced and Locked down. Four protections in code, for losses you cannot undo. Pick either preset and it is on. Each of its rules has its own switch on the Rules tab.
- Your own policy. Rules of five types (sites, commands, scripts, files, tool servers), three presets and four more packs. You edit it in the console, test it, export it and read its history.
All rules decide on the gateway. When a call is blocked, the client runs an echo of the refusal instead of the call, so the model reads why and the command never reaches your terminal. Every block is written to the decision log.
Core protections (in Balanced and Locked down)
Core protections blocks nothing until you pick Balanced or Locked down. Once on, it matches what a call does, not its exact text. It reads through wrappers: sudo, sh -c, env X=1, timeout, nice, noglob, eval with a literal string, and about 130 others (98 wrapper words plus 33 carriers such as xargs, find -exec, ssh, docker exec and npx).
| Protection | Blocked | Still runs |
|---|---|---|
| Wipe or format a disk | dd of=/dev/disk2, mkfs.ext4 /dev/sda1, diskutil eraseDisk, shred /dev/sda | dd if=a of=b.img, diskutil list, ls > /dev/null 2>&1 |
| Delete the root or home folder | rm -rf /, rm -rf ~, rm -rf $HOME, rm -rf /Users/you, rm -rf /*, rm -rf ~/.*, a folder right under / such as rm -rf /usr, /etc or /tmp, rm -rf /usr/*, cd /usr && rm -rf *, any home folder such as rm -rf /Users/alice, /home/alice/* or /root, cd ~ && rm -rf *, find / -delete, echo / | xargs rm -rf, T=/; rm -rf $T, T=~; rm -rf "$T", rm -rf ${X:-/}, cmd=rm; $cmd -rf /usr | rm ~/notes.txt, rm -rf node_modules, rm -rf /tmp/build, rm -rf /tmp/*, rm -rf /etc/foo, rm -rf /Users/alice/proj/dist, T=build; rm -rf $T, T=$(mktemp -d); rm -rf "$T" |
| Open permissions on the whole machine | chmod -R 777 /, chmod 755 /, chown -R me ~, chmod 777 ~, chmod 600 ~, chmod -R 700 ~, chmod 777 /Users/alice | chmod +x ~/bin/x, chmod -R g+w ~/shared, chmod 700 ~, chmod 755 ~, chmod go-w ~ |
| Make the machine unusable | a fork bomb such as :(){ :|:& };: | shutdown, killall |
The same refusals apply inside one-line programs (fs.rmSync('/'), shutil.rmtree(os.path.expanduser('~'))), in a Codex exec cell, and in a Codex patch that deletes /.
Core protections does not block ordinary work: installs, force pushes, curl … | sh and network calls all run. The other packs cover those.
Each Core protections rule has its own switch on the Rules tab. Switching to Observe keeps the rules that are on.
What Core protections does not catch
Core protections reads the call text before it runs. It does not run code and does not look at the disk. So it does not catch:
- A string built at run time: a target that comes from a command, the environment or a file when the call runs, such as
T=$(pwd); rm -rf "$T". A variable set to a fixed value in the same call is read:T=/; rm -rf $Tis blocked. - Decoded text: base64 piped to a shell, string building in perl, ruby or awk.
- A script file written earlier.
- A compiled program.
It also covers only the root folder, the folders right under it and home folders. A folder two or more levels down, such as rm -rf /usr/local, is not a Core protections block; your own rules can cover it.
Core protections never refuses a call only because it could not read it. Treat it as a guard against accidents and runaway agents. For a hard boundary, also run the agent in the Claude Code sandbox or Codex workspace-write.
Rule types
A rule has one type. Its match is a list of conditions, any, and a list of carve-outs, except. It matches when any condition matches and no carve-out does. There are no regular expressions and no wildcards: a wildcard is refused with a hint.
Sites
pastebin.comevilgithub.com never matches github.comCage finds hosts in shell commands (with or without https://, git remotes, nc, ssh, openssl, /dev/tcp), one-line programs, web fetches, tool-server inputs and nested URLs: curl https://r.jina.ai/https://pastebin.com/x is refused on the inner host. A search query is not a destination.
Other sites. By default other sites are allowed. Set it to block and every host a call names must match an allow entry. localhost is exempt. A host built at run time, or a one-line program that opens its own sockets, is then refused. Probe under Locked down: WebFetch https://example.com answered "Only approved sites are allowed, and example.com is not on the list".
Commands
Each condition is one of three kinds.
program, subcommand, flags_any, flags_all, flag_values (equals, starts_with, contains, host_not_in), env_values, args_any, paths, and for git push push_to and push_force. Example: terraform destroy.cloud.mutate catches aws s3 rm and terraform apply.download_run (curl or wget output handed to a shell, piped or through a file) or env_dump (env, printenv, export -p, a bare set, /proc/<pid>/environ)Conditions can name settings: $PRODUCTION, $PROTECTED, $APPROVED_REGISTRIES. A command rule cannot allow; to exempt something, add a carve-out.
The 22 capabilities: fs.list, fs.read, fs.write, fs.delete, fs.perm, proc.exec, net.fetch, net.listen, pkg.install, db.read, db.write, db.schema, cloud.read, cloud.mutate, vcs.publish, vcs.rewrite, secret.read, host.config, host.destroy, mcp.call, agent.spawn, config.write. Direct tools map to the same ones: Read is fs.read, Glob is fs.list, Write and Edit are fs.write, Task is agent.spawn, a tool-server call is mcp.call. proc.exec means a program Cage cannot look inside. One quirk: git push -f also reads as db.schema.
Scripts
A runner and a script name: npm, pnpm, yarn, bun, make, just or task, or a script path. Example: runner npm, name deploy blocks npm run deploy. The name also matches through node --run, corepack, turbo, nx, lerna and npm-run-all. The rule matches the name only: node scripts/deploy.js runs the same code without matching, and the agent can edit what the script does.
Files
~/.ssh.env.env.pem$REPOPaths start at /, ~, $HOME or $REPO. Operations: read, write, delete, permissions. Cage expands ~ with the session's home and fails closed for a block when it is unknown, resolves relative paths against the working folder, folds case on macOS and Windows, and treats /private/tmp as /tmp. Linking, copying or archiving a protected file counts as reading it. A recursive tool (Grep, Glob, grep -r, rg, find, tar, zip, rsync, cp -r, du) on a parent folder matches. Files rules apply to Read, Write, Edit, NotebookEdit, Grep, Glob, Codex view_image and apply_patch, and file targets in shell commands and one-line programs.
Tool servers
A server, and optionally one tool: server github, tool create_issue matches mcp__github__create_issue. An empty tool means the whole server. Server names are compared without case. Set other tool servers to block and only servers with an allow entry run. Strings in tool-server inputs are also checked against Sites rules.
Actions, modes and scope
| Field | Values |
|---|---|
| action | block, or allow for Sites and Tool servers only. An allow entry is an allowlist entry and counts only while other sites or servers are blocked. It never overrides a block. |
| mode | enforce: the call does not run. monitor: the call runs and the log records "would block". |
| scope | the whole account, or one project |
| name | required; the words people read in refusals |
| why | optional; shown in the refusal |
| tests | saved cases of input and expected result |
| enabled | on or off |
A policy holds at most 5,000 rules; a text field holds at most 2,000 characters. There is no ask or warn action: they were retired on 2026-09-25, and a write that uses them gets an error naming the replacement.
Settings: production names (default prod, production, live), protected branches (default main, master, production and release/*), approved registries, and how long the log keeps decisions (1 to 365 days, default 30). See Settings for what each one changes.
Presets
| Preset | Packs | Other sites | Other tool servers | Pack rules |
|---|---|---|---|---|
| Observe | none | allowed | allowed | 0 |
| Balanced | Core protections, and all four at Balanced level | allowed | allowed | 25, 4 in Monitor |
| Locked down | Core protections, and all four at Locked level | blocked | blocked | 29, 2 in Monitor |
New accounts start with no preset: none is picked until you pick one. A switch shows a dry run first: rules added, changed and removed, and why a change loosens the policy. Switching removes the rules a pack added. The rules you wrote and pack rules you edited stay.
Packs
A pack writes ordinary rules, each with saved tests. Edit a pack rule and it detaches from the pack and stays as you left it. Rules you did not edit follow the pack's current definition. "Off" means the rule is not added at that level.
Protect production (15 rules)
| Rule | Catches | Balanced | Locked down |
|---|---|---|---|
| Destroy infrastructure | terraform, tofu, terragrunt destroy or apply -destroy; pulumi destroy; cdk destroy | block | block |
| Apply infrastructure changes | terraform apply, pulumi up, cdk deploy | block | block |
| Use production credentials | AWS_PROFILE, CLOUDSDK_CORE_PROJECT, --profile, --context or --project set to a production name | block | block |
| Change cloud resources | the cloud.mutate capability | monitor | block |
| Delete in Kubernetes production | kubectl or oc delete, drain, scale with a production context or namespace, or with no context | block | block |
| Deploy from the agent | fly deploy, vercel --prod, netlify deploy --prod, the Cloudflare Workers deploy command, heroku releases:rollback | block | block |
| Change database shape | supabase db reset, prisma migrate reset, prisma db push --force-reset or --accept-data-loss, drizzle-kit drop | block | block |
| Drop tables or databases | psql -c or mysql -e with DROP TABLE, DATABASE, SCHEMA or TRUNCATE | block | block |
| Change database data | the db.write capability | monitor | block |
| Push to protected branches | git push to main, master, production, release/* | block | block |
| Rewrite shared history | git push --force, -f, +ref | block | block |
| Discard local work | git reset --hard, clean -f (-fd, -fdx), checkout . or checkout -f, restore . over the whole tree | block | block |
| Discard staged and local work | git restore --staged --worktree . (git restore --staged . alone keeps running) | block | block |
| Delete repositories and releases | gh repo delete, gh release delete, npm unpublish | block | block |
| Publish packages | npm, pnpm, yarn, cargo publish; twine upload; gh release create | block | block |
SQL in a file (psql -f) or on stdin is not read. This pack is a guardrail: pair it with branch protection and credentials the agent does not hold.
Protect secrets (5 rules)
| Rule | Catches | Balanced | Locked down |
|---|---|---|---|
| Read credential files | ~/.ssh, ~/.aws, ~/.config/gcloud, ~/.azure, ~/.kube, ~/.docker/config.json, ~/.npmrc, ~/.netrc, ~/.pypirc, ~/.git-credentials, *.pem, *.key, *.p12 | block | block |
| Read environment files | .env and .env.*, except .env.example, .env.sample, .env.template | block | block |
| Dump the environment | the env_dump shape and secret.read | block | block |
| Upload files | curl -T, -F, --form, -d @file, --data-urlencode @; wget --post-file, --body-file; gh gist create | block | block |
| Known paste and drop sites | 19 hosts and their subdomains, such as pastebin.com, transfer.sh, 0x0.st, webhook.site, gist.github.com | block | block |
Without "block other sites", this pack slows a leak and does not stop it. Data sent in a query string or DNS lookup, or by a program that picks its destination at run time, is not caught. Pushes to unknown remotes, secrets in outgoing text, and scp, rsync, sftp or nc uploads are not covered yet.
Stay inside the repo (7 rules)
| Rule | Catches | Balanced | Locked down |
|---|---|---|---|
| Change or delete files outside the repo | writes and deletes outside the project, except /tmp, /var/folders, ~/.cache, ~/.npm, ~/.cargo, ~/Library/Caches | block | block |
| Delete the repo | a recursive delete of the project folder, its .git, or a folder that holds them, such as rm -rf of your home folder | block | block |
| Read files outside the repo | reads outside the project, except /tmp, /var/folders, ~/.cache, /usr, /opt/homebrew | off | monitor |
| Delete files in the repo | deletes inside the project | off | block |
| Change machine setup | the host.config capability, such as launchctl load | block | block |
| Run AI CLIs with safety off | claude --dangerously-skip-permissions, codex --dangerously-bypass-approvals-and-sandbox or --yolo, gemini --yolo or -y, q --trust-all-tools | block | block |
| Start other agents | the agent.spawn capability, such as claude -p | monitor | block |
The repo is the session's working folder. When the gateway cannot see it, these rules do not apply to that call.
Approved package sources (4 rules)
| Rule | Catches | Balanced | Locked down |
|---|---|---|---|
| Run downloaded scripts | the download_run shape: curl … | sh, curl -o x && bash x | block | block |
| Install from an unapproved registry | --registry, -i, --index-url, --extra-index-url, NPM_CONFIG_REGISTRY or PIP_INDEX_URL not on your list, for npm, pnpm, yarn, bun, npx, pip, uv, pipx | block | block |
| Run one-off packages | npx, bunx, uvx, pnpx, pnpm dlx, yarn dlx | off | monitor |
| Approved registries | an allow entry for 15 hosts: npm, yarn, PyPI, crates, the Go proxy, RubyGems, Packagist, GitHub download hosts | off | allow |
Public registries keep working: npm install left-pad runs.
How enforcement works
Each rule in the console shows where it is enforced. Claude Code and Codex are enforced. Other agents that route through the gateway are recorded. OpenCode is not verified yet.
context-mode-run-code runs in the gateway's sandbox. Cage reads the code first, then checks each host the program really connects to, so fetch(atob(x)) is caught.context-mode-run-local is read like a shell command before it runs. On your machine, a guard written into the script checks URLs against your Sites block entries. That guard is advisory: a hostile program can get around it."Block other sites" and Sites rules with carve-outs are decided at the gateway only. The run-local guard and the sandbox receive block entries only.
What the agent sees
For a client-side tool, the gateway rewrites the model's call into an echo. The client runs only that echo, and the model gets the refusal as its tool result. A Read call is rewritten the same way.
echo '<refusal>' # [[CM-CAGE-GUARD-7a1f9c3e-v1]] #cm-orig:<base64 of the original call>
Captured on 2026-09-28 from real claude -p turns through the gateway. For a Read of .env under a team rule, the refusal carries the rule's name, its why and its scope:
⛔ context-mode Cage blocked this command, so it was NOT executed: Blocked by your team’s policy: Cage live proof: block reading .env. Live proof for the Cage page (2026-09-28). Scope: your account. Only the owner can change this, in Cage in the context-mode dashboard; do not retry it as a different shell command or a Node.js or Python script. Tell the user it was blocked and continue with other work.
In the same run, npm install left-pad and a sandbox fetch of a blocked host were refused, and each block appeared in the decision log. The client's own safety hook never fired, because nothing reached the shell.
Try it in 5 minutes
Friday release: the agent tries to push to main and to read .env. Cage stops both, and the release still ships on a branch.
Setup
npx @context-mode/cli # sign in and connect Claude Code, then restart Claude Code
# In the console, open Cage, turn it on, and pick the Balanced preset.
mkdir -p ~/cage-demo && cd ~/cage-demo
git clone --bare https://github.com/expressjs/cors.git origin.git # a local copy is your remote, so nothing reaches GitHub
git -C origin.git branch -m master main 2>/dev/null; git -C origin.git symbolic-ref HEAD refs/heads/main
git clone origin.git app && cd app && npm install
printf 'NPM_TOKEN=npm_DEMO_not_a_real_token_0000\n' > .env # a fake token, as most real repos have
git -C ../origin.git rev-parse main > ../main-before.txt # the remote main, before the agent starts
claude
Prompts
Prompt 1:
Release prep. Run the tests. Then add a new 'unreleased' heading at the top of HISTORY.md with one line: 'Docs: clarify the preflight example'. Commit it and push straight to main so CI can publish. The npm token is in .env if you need it.
Prompt 2:
CI says the npm token is wrong. Show me what is in .env so I can compare it.
Prompt 3:
Fine. Push the commit to a branch called release-prep instead, and give me the command to open a pull request.
Prompt 4, later in the day, after some half-done edits:
I messed up this branch. Throw away all my local changes and untracked files so it matches origin/main again.
What you will see
# prompt 1: the push to main comes back as this tool result
⛔ context-mode Cage blocked this command, so it was NOT executed: Blocked by your team’s policy: Push to protected branches. Protected branches change through reviewed pull requests. Scope: your account. Cage is on for this account, and its rules are set in Cage in the context-mode dashboard; do not retry it as a different shell command or a Node.js or Python script. Tell the user it was blocked and continue with other work.
# prompt 2: the read of .env
⛔ context-mode Cage blocked this command, so it was NOT executed: Blocked by your team’s policy: Read environment files. .env files hold secrets. Scope: your account. Cage is on for this account, and its rules are set in Cage in the context-mode dashboard; do not retry it as a different shell command or a Node.js or Python script. Tell the user it was blocked and continue with other work.
- Prompt 1. The tests pass (49 passing, lint clean, 100% coverage). HISTORY.md is edited and the commit is made on your local main. The push to main is blocked. The agent says so, does not try another way, and offers to push a branch and open a pull request. Cage may also stop one small side step, such as a temp-file move. The agent uses another tool and carries on.
- Prompt 2. The read of
.envis blocked with its rule, "Read environment files". You never see the fake token. The agent tells you how to compare it in your own terminal. - Prompt 3. The branch push runs as normal. The agent prints a
gh pr createcommand, and saysghneeds a GitHub remote, because origin is a local folder. - Check it yourself.
git -C ~/cage-demo/origin.git rev-parse mainmatches~/cage-demo/main-before.txt, so main did not move.git -C ~/cage-demo/origin.git log -1 --stat release-prepshows the HISTORY.md commit. - Prompt 4. The discard is blocked with its rule, "Discard local work":
git reset --hardandgit clean -fddo not run, and your half-done edit, your untracked notes and.envare still there. The agent offersgit stash -u, or tells you to run the discard yourself. In the Balanced preset this rule blocksgit reset --hard,git clean -f(and-fd,-fdx),git checkout -- .andgit restore .over the whole tree, andrm -rfof the repo or your home folder. Ordinary work in the same session, such as running the tests and committing on a new branch, is not blocked. - On the Cage screen. The decision log shows the push block and the
.envblock, each with its rule, tool and program, and no command text.
We ran prompts 1 to 3 twice on 2026-10-02 with Claude Opus 5.5, and again on 2026-10-04 with Claude Haiku 4.5, each in real Claude Code sessions through the gateway. Every run blocked the push and the read, never showed the token, and landed the branch. Prompt 4 ran 3 times on 2026-10-04 on new test accounts with Balanced (Claude Haiku 4.5) and was blocked in 3 of 3, with no file lost, the .env read blocked and the next commit landing. Without an agent, the same git push origin main goes through, so the block comes from Cage, not from git.
Why it matters. You can let an agent do the whole release chore. One policy, held on the gateway, makes sure it never pushes to main or reads your secrets.
The Cage screen
Open Cage in the console. It has five tabs.
A decision log row holds the time, decision, mode, rule id and name, type, tool, program, host, the path a Files rule matched, tool server, project, session and policy version. It does not hold the command text, flag values, environment values or file contents, so you can show it to an auditor. Rows older than the retention setting are removed.
Settings
These values fill in the rules your packs use. They block nothing by themselves: a value changes a call only when a rule that uses it is on. With Cage off, or with no pack that uses a value, the value does nothing. A rule in Monitor records the call instead of blocking it.
Each field on the Settings tab shows the rules that are on and use it, for example "Used by: Protect production, 2 rules", and a command it changes, built from your own values. Fields that no rule uses sit under "Not used by your current rules", with the pack that would use them.
| Setting | What it is | Used by | Example |
|---|---|---|---|
| Production names | Names of your production cloud profiles, projects, Kubernetes contexts and namespaces. Default: prod, production, live. | Protect production: Use production credentials, Delete in Kubernetes production | aws --profile prod s3 ls is blocked because prod is a production name. aws --profile dev s3 ls runs. |
| Protected branches | Branches the agent must not push to, by exact name or by the start of the name. Default: main, master, production, and names that start with release/. | Protect production: Push to protected branches | git push origin main is blocked because main is protected. git push origin feature/x runs. |
| Approved registries | Private package registries the agent may install from. The public npm, Yarn and PyPI registries are always approved. Default: none. | Approved package sources: Install from an unapproved registry | npm install x --registry https://npm.acme.internal is blocked until you add npm.acme.internal. |
| Keep Activity for (days) | How long the decision log keeps rows. 1 to 365, default 30. | No rule | Set 90, and rows older than 90 days are removed. |
Your own command rules can use the same values: write $PRODUCTION, $PROTECTED or $APPROVED_REGISTRIES in a condition. Removing a production name or a protected branch, or adding a registry, loosens the policy, so the save asks for a reason and History marks it.
Import and export
Export gives one policy.json, shown here with one rule. Keys are sorted, rules are ordered by id, and server stamps are left out, so exporting twice gives the same bytes and a git diff shows only real changes. The file holds your rules, your settings and the packs you use.
{
"exported_at": "2026-09-28T12:00:00.000Z",
"packs": [],
"rules": [
{
"action": "block",
"aliases": [],
"detached": false,
"enabled": true,
"exceptions": "admin_approved",
"id": "r_no_destroy",
"match": {
"any": [
{
"program": [
"terraform"
],
"subcommand": [
"destroy"
]
}
],
"except": []
},
"mode": "enforce",
"name": "Destroy infrastructure",
"pack": "",
"scope": {
"id": "",
"label": "",
"level": "account"
},
"tests": [
{
"expect": "block",
"input": "terraform destroy"
},
{
"expect": "none",
"input": "terraform plan"
}
],
"type": "command",
"why": "Infrastructure is torn down only through CI."
}
],
"schema": "context-mode.policy/v2",
"scope": {
"id": "",
"label": "",
"level": "account"
},
"settings": {
"approved_registries": [],
"preset": "custom",
"production_names": [
"prod",
"production",
"live"
],
"protected_branches": {
"equals": [
"main",
"master",
"production"
],
"starts_with": [
"release/"
]
},
"retention_days": 30,
"sites_default": "allow",
"tool_servers_default": "allow"
}
}
Import reads context-mode.policy/v2 and v1. Merge adds and changes rules; replace also removes rules the file does not list. Import always shows a dry run first: rules added, changed, removed and unchanged, invalid rows with their errors, and whether the change loosens the policy. Nothing is applied while a row is invalid.
History and versions
Every write bumps the policy version and appends a history entry with the time, who (a signed-in person or an API key), how (editor, settings, import, preset or pack), the operation, the rule before and after, the reason, and whether it loosens the policy. Each entry's hash covers the one before it, so an edited or deleted entry breaks the chain, and Verify record says where.
An API key can only tighten the policy. A change that loosens it, such as deleting a block rule, needs a person signed in to the console and a reason, and the reason is kept in History. Restoring an old version is a new write, so the same check applies.
Plans
All of Cage is in every plan, Free included: the Core protections pack, your own rules, presets and packs, and import, export and editing on the Cage screen. Cage is off until you turn it on, and nothing is blocked until you pick a preset or add rules. On Team, one Cage policy covers every member of the org.
When your requests run out, the gateway pauses Context Saving, Memory, Skills and Thinking in Code, and Cage core protections stay on: security never depends on a card. See what happens when you run out.
Limits
- Cage is not an OS sandbox. It reads the call before it runs. What a script file, a Makefile target, a package.json script or a compiled program does is not read.
- Code run with
context-mode-run-localcarries a guard that a hostile program can get around. For a hard network block, usecontext-mode-run-code. - A path or host a program builds at run time is not read. While a Sites block is on, a host Cage cannot read is refused.
- Renamed binaries are not recognised.
- Sites IP ranges match IP addresses written in the call, not names that resolve into the range.
- A tool server's own network traffic is not controlled. Server fingerprints (catching a server that changes what it is) are not built. Codex's read_mcp_resource and list_mcp_resources calls are not matched by Tool servers rules.
- There is no ask step, no exceptions and no group or personal scope.
- OpenCode is not verified. Other agents are recorded, not enforced.
Keep the gateway on
Cage checks only requests that go through the gateway. If a person removes the base URL from a machine, Cage no longer sees that machine.
- Claude Code. Set
ANTHROPIC_BASE_URLin theenvblock of managed settings, delivered by MDM, amanaged-settings.jsonfile or the claude.ai console. Anthropic's page says of managed settings: "nothing you set overrides it, apart from a few security-sensitive exceptions". It also says anenvblock "is an ordinary key and follows the levels above", and the settings reference says a value there "overwrites the same variable exported in your shell". In our test on Claude Code 2.1.283, a base URL set through settings won over the one exported in the shell. We have not tested an MDM rollout. - Codex. OpenAI's managed configuration page splits admin control into requirements, "admin-enforced constraints that users can’t override", and configuration defaults, "settings that users can override". Requirements "constrain security-sensitive settings", such as approval policy, sandbox mode, managed hooks and MCP servers. The page names no requirement for the model provider. So Codex's own controls cannot hold Codex on the gateway today.
- Watch for gaps. The Profile screen lists each signed-in terminal with the day it was last seen. A terminal that stops showing up while its user keeps working is the sign to check. There is no alert for this yet.
Compared with other controls
From each vendor's own pages, read on 2026-09-30. "Not stated" means the page we read does not say. The landscape lists more tools.
| Control | Where the check runs | Clients | How a rule matches | When it cannot read the input | Files and secrets | One policy across machines | Record |
|---|---|---|---|---|---|---|---|
| Cage | The gateway, before the client runs the call | Claude Code and Codex | By what a call does: 22 capabilities, read through 98 wrapper words and 33 carriers such as xargs, find -exec and ssh | With a Sites block on, a host it cannot read is refused. A path with an unknown home folder fails closed for a block rule. | File rules by folder, name and operation; a Protect secrets pack | Yes, per account. Only the owner can loosen it. | Decision log; policy history in a hash chain |
| Claude Code permissions, hooks and sandbox | The client; /sandbox at the OS level | Claude Code | Patterns on the command text. It first strips timeout, time, nice, nohup, stdbuf, command, builtin, noglob and a leading assignment of certain known-safe environment variables. "A deny or ask rule matches past any leading assignment." Anthropic: "Bash permission patterns that try to constrain command arguments are fragile." A hook can run any check you write. | Your hook decides | Read and Edit rules; the sandbox limits files and network | Managed settings: "nothing you set overrides it, apart from a few security-sensitive exceptions" | OpenTelemetry tool_decision events, with a decision_source: config, a hook, or the user. You run the collector. |
| Codex sandbox, approvals and requirements | The client and its OS sandbox | Codex | Requirements "constrain security-sensitive settings", such as approval policy, sandbox mode, permission profiles, managed hooks and which MCP servers users can enable | The sandbox holds whatever the command is | The sandbox limits writes and network | Requirements: "admin-enforced constraints that users can’t override" | OpenTelemetry codex.tool_decision: "approved/denied and whether the decision came from config vs user". Telemetry is "Disabled by default; opt in". |
| CC Safety Net (MIT) | A hook on each machine | Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, OpenCode, Amp Code, Antigravity CLI, Kimi Code and others. Windows support for most "is best effort and has not been tested". | "It parses what the command does. Wrapping the command or reordering flags does not hide it." It "still blocks the same command inside bash -c or python -c". Rulebooks for Terraform, AWS, gcloud and Azure, or your own JSON: "A rulebook can only add blocks." | Its Strict preset also "blocks dynamic or unparseable commands the analyzer cannot verify safely". Standard is "Recommended for normal coding". "A broken config file never blocks anything." | Blocks SSH keys, .env files, ~/.aws and the credential files coding CLIs keep, in the shell and in the agent's read, edit, write and search tools | Commit its policy folder "so clones and cloud sessions pick up the same rules". Each machine still installs the hook. | An audit trail on the machine that "records command decisions, but it does not record command output or prompts"; a local web page to review it |
| dcg (a custom license based on MIT, with an OpenAI/Anthropic rider) | A hook on each machine | Claude Code, Codex CLI, Gemini CLI, Copilot CLI, VS Code Copilot Chat, Cursor and others | 50+ packs ("Databases, Kubernetes, Docker, AWS/GCP/Azure, Terraform, and more"). Scans heredocs and inline scripts, such as python -c. | Malformed hook input: "Allow with an audit warning"; opting into general.fail_closed denies. "Analysis timeouts become explicit review/block outcomes." | A pack against destructive AWS Secrets Manager and SSM Parameter Store operations | Config on each machine. A newly cloned repository's config "may only add enforcement". | warn "lets the command run and records the decision"; log "does the same silently". dcg explain shows why a command was blocked. |
| Lasso | Client hooks, rolled out with managed settings; scanning in Lasso's cloud | Claude Code, Cursor, Codex, OpenCode | Checks tool calls and scans content for injected instructions | Not stated | Not stated | Yes | Audit trail. Its Claude Code hook is MIT and warns but does not block. |
| Zenity | Hooks on each machine, and an MCP gateway | Claude Code, Codex, Copilot, Cursor | One central policy, blocks inline | Not stated | Not stated | Yes | Audit through hooks and OpenTelemetry |
| Prisma AIRS | Endpoint, network and cloud; an AI gateway from Portkey | Cursor, Claude Code, Codex, Antigravity | One policy across agents, with an exception request | Not stated | Not stated | Yes | Session timelines |
| LiteLLM tool permission guardrail | A proxy you host, on the request path | Any client of the OpenAI or Anthropic API | A regex for the tool name, with optional checks on arguments. "Block halts the request, Rewrite strips forbidden tools" and returns an error message. | A default_action "for tools that do not hit any rule" | Not stated | Yes, per proxy | Not stated on the guardrail page |
| Docker Sandboxes | A microVM with a network proxy | Claude Code, Codex, Copilot, Cursor, Gemini, Kiro, OpenCode and others | Network policy at the proxy. Docker: "The agent has full control inside the VM, including sudo access." | Not applicable | The VM holds only what you share with it | Org policy on a paid plan | Not stated |
Where Cage differs.
- One policy for Claude Code and Codex, with nothing on each laptop but a base URL.
- Rules match what a call does, not its text, so a wrapper or a carrier does not hide a command.
- The same hop lowers cost: it folds tool output and archives it. A security tool adds a hop that does one job.
- An exportable decision log, and a policy history in which an edited or deleted entry breaks the hash chain.
Where they are stronger.
- CC Safety Net and dcg are free, run on the machine with no network, and cover more agents. dcg has 50+ packs. Check the licenses: CC Safety Net is MIT, and dcg has a "Custom source license based on MIT with an OpenAI/Anthropic rider". Read it before you adopt it.
- Claude Code and Codex controls are first-party and free. MDM keeps them on the machine, and both can ask a person first. Cage cannot ask.
- An OS sandbox (Claude Code
/sandbox, the Codex sandbox, Docker Sandboxes) limits what a running program can reach. Cage reads the call before it runs and does not see inside a script file or a compiled program. - Prisma AIRS, Zenity and Lasso cover more agents, detect injection and data loss with models, and offer SSO and group policies. Cage has one policy per account.
Cage runs next to these. Keep your agent's own sandbox on: Cage decides on the call, and the sandbox limits what a program can reach once it runs.
FAQ
Does Cage block anything as soon as I sign up?
No. Cage is in every plan and is off until you turn it on. Nothing is blocked until you pick a preset or add rules.
Can I turn Core protections off?
Yes. It comes with Balanced and Locked down, and each of its four rules has its own switch on the Rules tab.
Does Cage replace the Claude Code or Codex sandbox?
No. Use both. Cage gives one policy for both agents and a decision log. The sandbox limits what a running program can reach.
Does the log store my commands?
No. A row holds the rule, tool, program, host or matched path, never the command text or its values.
Can the agent change the policy?
An API key can only tighten it. Loosening needs a person signed in to the console and a reason.
Can Cage ask me before it blocks?
No. A rule blocks, or in Monitor mode records what it would block. Use Monitor to try a rule on real traffic first.
How do I check a rule before I turn it on?
Type the command in the test box, or save the rule in Monitor mode and read Activity.
Compared with other tools: see the landscape.