Defense-in-depth proxy sandbox for AI agents
Project description
agentcage
Defense-in-depth proxy sandbox for AI agents.
Sandboxed container environments for AI agents, powered by rootless Podman and mitmproxy.
⚠️ Warning: This is an experimental project. It has not been audited by security professionals. Use it at your own risk. See Security & Threat Model for details and known limitations.
Setting up OpenClaw? See the OpenClaw guide and
openclaw/config.yaml.
What is it?
agentcage is a CLI that generates hardened, sandboxed container environments for AI agents. It produces systemd quadlet files that deploy three containers on a rootless Podman network -- no root privileges required. Your agent runs on an internal-only network with no internet gateway; the only way out is through an inspecting mitmproxy that scans every HTTP request before forwarding it.
Why is it needed?
Most AI agent deployments hand the agent a lethal trifecta:
- Internet access -- the agent can reach any server on the internet.
- Credentials -- API keys, tokens, and secrets are passed as environment variables or mounted files.
- Arbitrary code execution -- the agent runs code it writes itself, or code suggested by a model.
Any one of these alone is manageable. Combined, they create an exfiltration risk: if the agent is compromised, misaligned, or simply makes a mistake, it can send your credentials, source code, or private data to any endpoint on the internet. Most current setups have zero defense against this -- the agent has the same network access as any other process on the machine.
agentcage breaks the trifecta by placing the agent behind a defense-in-depth proxy sandbox: network isolation, domain filtering, secret injection, credential scanning, payload analysis, and container hardening -- all fail-closed. See Security & Threat Model for the full breakdown of each layer and known limitations.
How does it work?
The agent container has no internet gateway. All HTTP traffic is routed via HTTP_PROXY / HTTPS_PROXY to a dual-homed mitmproxy container, which is the agent's only path to the outside world. A pluggable inspector chain evaluates every request -- enforcing domain allowlists, scanning for secret leaks, and optionally analyzing payloads -- before forwarding or blocking with a 403.
See Architecture for the full container topology, inspector chain, startup order, and certificate sharing.
Prerequisites
Linux
Arch Linux:
sudo pacman -S podman python uv
Debian / Ubuntu (24.04+):
sudo apt install podman python3
curl -LsSf https://astral.sh/uv/install.sh | sh
Fedora:
sudo dnf install podman python3 uv
macOS
brew install podman python uv
podman machine init
podman machine start
Note: On macOS, Podman runs containers inside a Linux VM.
podman machine initcreates andpodman machine startstarts it.
Install
uv tool install agentcage # from PyPI (when published)
uv tool install git+https://github.com/agentcage/agentcage.git # from GitHub
Or for development:
git clone https://github.com/agentcage/agentcage.git
cd agentcage
uv run agentcage --help
Quick Start
# Edit config (set your allowed domains, image, etc.)
cp examples/basic/config.yaml config.yaml
# Store secrets first (they're required before cage creation)
agentcage secret set myapp ANTHROPIC_API_KEY
# Create the cage (builds images, generates quadlets, starts containers)
agentcage cage create -c config.yaml
# Verify everything is healthy
agentcage cage verify myapp
CLI Reference
The CLI is organized into two command groups: cage (manage cages) and secret (manage cage-scoped secrets).
agentcage <group> <command> [options]
cage -- Manage cages
| Command | Description |
|---|---|
cage create -c CONFIG |
Build images, generate quadlets, install, and start a new cage |
cage update NAME [-c CONFIG] |
Rebuild images and restart an existing cage |
cage list |
List all cages with status |
cage destroy NAME [-y] |
Stop containers, remove quadlets, state, and scoped secrets |
cage verify NAME |
Health checks (containers, certs, proxy, egress, rootless) |
cage reload NAME |
Restart containers without rebuilding images |
secret -- Manage cage-scoped secrets
| Command | Description |
|---|---|
secret list NAME |
List secrets for a cage (with status if cage exists) |
secret set NAME KEY |
Set a secret (prompts for value or reads stdin) |
secret rm NAME KEY |
Remove a secret |
cage create
agentcage cage create -c <config>
Creates a new cage from a config file. This single command:
- Validates the config
- Checks that all required secrets exist in Podman
- Saves deployment state to
~/.config/agentcage/deployments/<name>/config.yaml - Builds the proxy and DNS container images
- Generates and installs 5 quadlet files into
~/.config/containers/systemd/ - Reloads systemd and starts the cage
The generated quadlet files are:
<name>-net.network-- internal network with fixed subnet<name>-certs.volume-- shared certificate volume<name>-dns.container-- DNS sidecar (dnsmasq)<name>-proxy.container-- mitmproxy with inspector chain<name>-cage.container-- your agent container
Fails if any required secrets are missing. The error message tells you exactly which secrets to create:
error: missing secrets for cage 'myapp':
ANTHROPIC_API_KEY
Create them with:
agentcage secret set myapp ANTHROPIC_API_KEY
cage update
agentcage cage update <name> [-c <config>]
Rebuild and restart an existing cage. Use this after changing code or config:
- With
-c: Updates the stored config, then rebuilds and restarts. - Without
-c: Rebuilds from the previously stored config (useful when only the container image or proxy code has changed).
Stops the running services before rebuilding, then starts them again.
cage list
agentcage cage list
Lists all known cages with their current status:
NAME STATUS
myapp running (3/3)
testcage stopped (0/3)
broken degraded (2/3)
cage destroy
agentcage cage destroy <name> [-y|--yes]
Tears down a cage completely:
- Stops all containers (cage, proxy, DNS)
- Removes quadlet files from
~/.config/containers/systemd/ - Removes the Podman network and certificate volume
- Removes all scoped secrets (e.g.,
myapp.ANTHROPIC_API_KEY) - Removes deployment state from
~/.config/agentcage/deployments/<name>/
User-defined named volumes and bind-mounted data are never removed. Pass -y to skip the confirmation prompt.
cage verify
agentcage cage verify <name>
Runs health checks against a running cage:
- All 3 containers running (cage, proxy, DNS)
- CA certificate present in the shared volume
HTTP_PROXY/HTTPS_PROXYset in the cage container- Egress filtering working (blocked domain returns 403)
- Podman running rootless
Example output:
=== agentcage verify: myapp ===
-- Containers --
[PASS] myapp-proxy is running
[PASS] myapp-dns is running
[PASS] myapp-cage is running
-- CA Certificate --
[PASS] mitmproxy CA cert exists in shared volume
-- Proxy Configuration --
[PASS] HTTP_PROXY is set
[PASS] HTTPS_PROXY is set
-- Egress Filtering --
[PASS] Blocked domain (evil-exfil-server.io) is denied (HTTP 403)
-- Podman --
[PASS] Podman is running rootless
=== Results: 8 passed, 0 failed, 0 warnings ===
cage reload
agentcage cage reload <name>
Restarts containers without rebuilding images. Useful after config-only changes (the config YAML is bind-mounted into the proxy container, so a restart picks it up).
secret set
agentcage secret set <name> <key>
Sets a deployment-scoped secret. When run interactively, prompts for the value with hidden input. Also accepts piped input:
# Interactive (prompts for value)
agentcage secret set myapp ANTHROPIC_API_KEY
# Piped from a command
echo "sk-ant-abc123" | agentcage secret set myapp ANTHROPIC_API_KEY
# From a file
agentcage secret set myapp ANTHROPIC_API_KEY < /path/to/key.txt
Secrets are stored in Podman as <name>.<key> (e.g., myapp.ANTHROPIC_API_KEY) and mapped back to the original env var name via target= in the quadlet templates, so the container sees ANTHROPIC_API_KEY as expected.
If the cage is currently running, it is automatically reloaded after the secret is set.
secret list
agentcage secret list <name>
Lists secrets for a cage. If the cage has deployment state, cross-references with the config to show expected secrets and their status:
NAME TYPE STATUS
ANTHROPIC_API_KEY injection ok
GITHUB_TOKEN direct MISSING
Secret types:
- injection -- managed by the proxy's secret injection system (the cage sees a placeholder; the proxy swaps in the real value)
- direct -- passed directly to the cage container via
podman_secrets
If no deployment state exists, lists all Podman secrets matching the <name>. prefix.
secret rm
agentcage secret rm <name> <key>
Removes a secret from Podman. If the cage is currently running, it is automatically reloaded.
Typical Workflow
# 1. Write your config
cp examples/basic/config.yaml config.yaml
vim config.yaml
# 2. Store secrets (before creating the cage)
agentcage secret set myapp ANTHROPIC_API_KEY
agentcage secret set myapp GITHUB_TOKEN
# 3. Create the cage
agentcage cage create -c config.yaml
# 4. Verify it's healthy
agentcage cage verify myapp
# 5. View logs
journalctl --user -u myapp-cage -f
# 6. Update after code/config changes
agentcage cage update myapp -c config.yaml
# 7. Rotate a secret (auto-reloads the cage)
agentcage secret set myapp ANTHROPIC_API_KEY
# 8. Restart without rebuild (config-only change)
agentcage cage reload myapp
# 9. Tear it all down
agentcage cage destroy myapp
View logs
journalctl --user -u myapp-cage -f
journalctl --user -u myapp-proxy -f
journalctl --user -u myapp-dns -f
Deployment State
agentcage tracks each cage in ~/.config/agentcage/deployments/<name>/config.yaml. This stored config copy allows commands like cage update (without -c) and cage reload to operate without requiring the original config file. The state is removed when a cage is destroyed.
Architecture
Three containers on an internal Podman network: the agent (no internet gateway), a dual-homed DNS sidecar, and a dual-homed mitmproxy that inspects and forwards all traffic. See Architecture for the full topology, inspector chain, startup order, and certificate sharing.
Configuration
See the Configuration Reference for all settings, defaults, and examples. Example configs: basic/config.yaml | openclaw/config.yaml
Security
The agent has no internet gateway -- all traffic must pass through the proxy, which applies domain filtering, secret detection, payload inspection, and custom inspectors. See Security & Threat Model for the full threat model, defense layers, and known limitations.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentcage-0.1.2.tar.gz.
File metadata
- Download URL: agentcage-0.1.2.tar.gz
- Upload date:
- Size: 504.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d22c5551107d3d596772ec2a80cd966833d9e4c9883cb46fb08e4e790460baf
|
|
| MD5 |
1075b15c8f2282155fbefce7fa6cbf09
|
|
| BLAKE2b-256 |
b3c9c3f5f87a5ac769ebab4aa0ec1d0cb29d5d9490764ee6df3ade6a272eb34d
|
Provenance
The following attestation bundles were made for agentcage-0.1.2.tar.gz:
Publisher:
publish.yml on agentcage/agentcage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentcage-0.1.2.tar.gz -
Subject digest:
6d22c5551107d3d596772ec2a80cd966833d9e4c9883cb46fb08e4e790460baf - Sigstore transparency entry: 957460681
- Sigstore integration time:
-
Permalink:
agentcage/agentcage@78f93ede351a13d9824db071a858a105f4c3ff8e -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/agentcage
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@78f93ede351a13d9824db071a858a105f4c3ff8e -
Trigger Event:
push
-
Statement type:
File details
Details for the file agentcage-0.1.2-py3-none-any.whl.
File metadata
- Download URL: agentcage-0.1.2-py3-none-any.whl
- Upload date:
- Size: 40.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e174047aaeee3869bf8593e8baee587e4166a5e998a331991bf72fc2bf86e705
|
|
| MD5 |
0bccf1848cb1b5879bffccfc2cb98e65
|
|
| BLAKE2b-256 |
43501b6563ecc904e94cf40bc0b4381059eb52062ca13af1a7dbcd70326f5477
|
Provenance
The following attestation bundles were made for agentcage-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on agentcage/agentcage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentcage-0.1.2-py3-none-any.whl -
Subject digest:
e174047aaeee3869bf8593e8baee587e4166a5e998a331991bf72fc2bf86e705 - Sigstore transparency entry: 957460685
- Sigstore integration time:
-
Permalink:
agentcage/agentcage@78f93ede351a13d9824db071a858a105f4c3ff8e -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/agentcage
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@78f93ede351a13d9824db071a858a105f4c3ff8e -
Trigger Event:
push
-
Statement type: