CAGE: Credential-bound Agent Gating Engine
Credential containment for AI agents. Capability-token-gated, structurally refusing, audit-chained.
We don't filter what the agent says. We cage what the agent can do.
CAGE is AI control infrastructure: the authority layer that decides what an autonomous agent is allowed to do. It sits above secret stores and HSMs, and away from prompt filters. It replaces the persistent API key with short-lived, scope-bound capability tokens, optionally bound to a context. With the broker deployed as DEPLOY.md describes, a prompt-injected or otherwise compromised agent never holds the credential it works with.
What CAGE is
The engine is Rust. Python is the first client. On the Rust backend (the bundled native extension) the master key lives only in Rust-owned memory and never crosses into Python as plaintext. Python is a thin binding that holds capability tokens. It came first because the AI-agent ecosystem (LangChain, the provider SDKs) lives there, and the broker speaks a language-neutral protocol, so clients in other languages can follow. The out-of-process broker goes further: the agent runs in a separate process holding only token IDs, and the key never enters its address space.
- A Rust-core secret vault. ChaCha20Poly1305 AEAD, zeroize-on-drop, and
mlock(2)to keep the master key pinned in RAM. - Structural refusal. No secret-bearing operation (an LLM call, a signature, a release signature) runs without a valid capability token for that verb. Tokens are TTL-bound, scope-bound, use-count-bound and revocable. Issuance, use, revocation and the refusals on the secret-bearing and control paths are recorded on a tamper-evident audit chain.
- A frozen issuance policy that bounds the issuer as well as the agent, where the operator provisions one. The broker reads it from
CAGE_ISSUANCE_SCOPESat start. Allowed scope namespaces and the TTL, use and per-scope lifetime ceilings are then fixed, and no setter exists. A remote caller who later reaches the control plane, even with a stolen operator key, cannot widen them. An out-of-policy scope is refused and audited, TTL and use counts are clamped, and the policy's fingerprint is recorded on every issued token. The lifetime ceiling is counted per running instance, so a restart starts it from zero. A malformed policy variable stops the broker at start. The policy is a broker feature: the in-process vault and the pure-Python gate have none. - A rustls-direct transport for the Anthropic Messages API and OpenAI Chat Completions. The decrypted API key lives only in
Zeroizing<Vec<u8>>buffers inside Rust and never crosses into Python. Each provider trusts only root CAs from the organizations named in its DNS CAA records. A secret can be pinned to one provider. - An append-only SHA-256 Merkle audit chain of token lifecycle events (issued, used, revoked, rejected), each with a sequence number. In the in-process API, signed checkpoints (
checkpoint()andverify_audit_chain_extends()) let a head anchored off-box prove the chain was extended and not truncated or rewritten. The broker offers a signed attestation over its chain head. Automatic anchoring to Sigstore Rekor is planned.
What CAGE is not
- A prompt filter or content-moderation gateway. Lakera, Lasso and NeMo Guardrails inspect what the agent says. CAGE constrains what the agent can do.
- A replacement for HashiCorp Vault or AWS Secrets Manager. CAGE sits above them and decides whether a given agent action is permitted at all, wherever the underlying secret is stored.
- Finished. It is pre-1.0. The known gaps are listed below.
How to think about CAGE
Three terms from capability security and the agent-safety literature place CAGE:
- It abolishes ambient authority. An agent handed an API key has ambient authority: standing, unscoped power over everything that key can do, usable at any time and invisibly. CAGE replaces it with capability tokens that are verb-bound, short-lived, use-counted and revocable. Authority stops being something the agent holds and becomes a decision made at each action.
- It keeps the agent secretless. With the broker, started and provisioned outside the agent, the agent's process holds only token IDs. The guarantee underneath is co-location: the authority check and the secret operation are one atomic Rust operation, with no ungated path to the credential. Short-lived tokens alone are policy. Co-location is mechanism.
- It removes the payoff of the lethal trifecta. Simon Willison names the dangerous combination as private data, untrusted content and the ability to communicate externally (the lethal trifecta). CAGE leaves all three in place and removes the reward: with the broker deployed as DEPLOY.md describes, an agent without a valid token, even one fully driven by a prompt injection, cannot reach the credential it would need to exfiltrate.
CAGE provides process verifiability: cryptographic evidence, on an append-only chain, of what an agent was authorized to do and what it attempted. Whether the agent did the right thing is a separate question for a separate layer. CAGE constrains capability and does not evaluate intent.
Quickstart
import cage gives you the pure-Python authority core, with no native extension and no third-party dependencies. It gates any action with a capability token and a tamper-evident audit chain:
import cage
gate = cage.Gate()
token = gate.issue_token(scope="db.write", context="job-42", ttl_seconds=60, max_uses=1)
gate.authorize_and_charge(token, "db.write") # authorize + charge one use atomically, or raise CageViolation
do_the_write() # your gated action
The secret-custody and LLM transport ring loads only when you reach for it. LLM calls need the bundled native extension, and the pure-Python fallback refuses them:
from cage import vault
vault.store_secret("llm_api_key", api_key) # encrypts into Rust-owned memory
# Fail-closed by default: unseal once for the bootstrap (the authority root),
# then pass it to every issue_token / revoke. On the Rust backend unseal()
# returns an opaque handle, so the secret stays in Rust memory.
# In production, broker_only_issuance=True routes issuance through the
# out-of-process broker so the authority never lives in your process.
bootstrap = vault.unseal()
token = vault.issue_token(
scope="llm.evaluate",
context="security-audit-run-42",
ttl_seconds=60,
max_uses=3,
bootstrap_token=bootstrap,
)
# The decrypted key never crosses into Python.
# provider= selects the transport ("anthropic" by default, "openai" also wired).
response = await vault.invoke_llm(token, {
"model": "claude-haiku-4-5",
"max_tokens": 200,
"messages": [{"role": "user", "content": "..."}],
})
For the strongest containment, run the out-of-process broker. The key lives in a separate process and the agent holds only token IDs. cage.broker.spawn_broker starts it, and cage.adapters.CagedAnthropic / CagedOpenAI take the provider SDKs' call shape (they return the provider's JSON as a dict). See examples/broker_demo.py, which drives a real agent child process that is refused every escalation, and examples/agent_broker_adapter.py. Read DEPLOY.md before running the broker in production: it only keeps the key from a same-user agent when the agent cannot control how the broker starts.
Threat model in one paragraph
CAGE is built to contain a compromised agent: an LLM agent driven by prompt injection, or agent code that turned hostile, running in its own process and possibly as the same OS user as the broker. Against that agent the broker keeps the key out of reach, provided the agent cannot write the broker binary, its launcher or its environment (see DEPLOY.md). A remote attacker holding the operator key is bounded by the frozen issuance policy, where one is provisioned. Root, the kernel, and anyone who controls how the broker starts are out of scope. CAGE does not claim to defeat an adversary with unrestricted code execution on the host. It aims to make credential compromise slow and visible.
Known limitations
These are the gaps as of v0.1. Reports and fixes are welcome (see SECURITY.md).
Host and deployment
- Root, a kernel exploit, or a process that controls how the broker starts (binary, launcher, environment,
LD_PRELOAD) can read or subvert the broker. On Linux the broker is dumpable betweenexecveand its own hardening call. Host configuration closes that window (DEPLOY.md). - A same-user agent can kill the broker. Audit entries not yet anchored off-box are lost, and a restart starts the issuance lifetime ceiling from zero.
- The broker socket is
0600, soCAGE_BROKER_ALLOWED_UIDShas no effect yet, and broker responses are not signed. mlockrequiresCAP_IPC_LOCK. Without it CAGE warns and continues, unlessCAGE_STRICT_MLOCKis set.
Key handling
- During an LLM call the decrypted provider key is resident from the decrypt until the request is written: up to about 50 s of network time on a stalled connection, longer if the process is stopped. That window is not mlocked.
- Plaintext request bytes transit rustls' reusable write buffer.
- TLS to the providers is classical. No provider offers post-quantum key exchange on its API endpoint yet.
- The vault's AEAD uses no associated data, so an attacker who can write the broker's memory can swap ciphertexts between secret names.
Authority and audit
- The Rust API lets a caller holding an invoke reservation settle it more than once. The Python and broker paths settle exactly once.
- A failed connection or TLS handshake refunds the use, with no cap, so a one-use token can retry a failing call indefinitely.
- An operator signature can be replayed if the broker's wall clock is rolled back.
- The provider pin binds a secret's name, and secrets are unpinned by default.
- The CA allowlist filters on the root CA only, by organization name. Subordinate CAs run by other organizations under an admitted root are accepted, and revocation is not checked.
- In-process,
register_scope_secretand the audit writer need no authority. In-process code is trusted. The broker exposes neither. vault.consume()still exists for scopes without a Rust transport. It is deprecated, refused by default, and will be removed before 1.0.
Verifying signed releases
Every CAGE wheel published on GitHub Releases is signed with Sigstore through GitHub Actions OIDC and Fulcio. The signing is keyless: no long-lived signing key exists in the repo or in CI. Every signature is recorded in the public Rekor transparency log.
To verify a downloaded wheel before installing:
cosign verify-blob \
--certificate cage_ai-X.Y.Z-*.whl.cert \
--signature cage_ai-X.Y.Z-*.whl.sig \
--certificate-identity-regexp \
"https://github.com/derdelean/cage/\.github/workflows/release\.yml@.*" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
cage_ai-X.Y.Z-*.whl
A passing verification proves the wheel was built by this repository's release workflow at the tagged commit, and the proof is publicly recorded in Rekor. The same workflow publishes to PyPI through trusted publishing (no API token), and PyPI shows a Sigstore attestation for each file. CAGE proposes credential containment through capability tokens and an audit chain, and its own releases are signed with the same Sigstore, Fulcio and Rekor model.
Installing
From PyPI (the distribution is named cage-ai, the import stays import cage):
pip install cage-ai
Or install a signed wheel from GitHub Releases (verify it first, as above), or build from source:
uv pip install maturin
maturin develop --release
A bare import cage (the authority core) needs no native extension. The vault and transport ring load it on first use.
To use CAGE from another local project, build a wheel and install it:
maturin build --release # -> target/wheels/cage_ai-0.1.1-*.whl
pip install target/wheels/cage_ai-0.1.1-*.whl
or reference the checkout as a path dependency:
# pyproject.toml of the consuming project (uv)
[tool.uv.sources]
cage-ai = { path = "../cage" }
Locally built wheels are not signed.
License and patents
CAGE is open-source under the Apache License 2.0. Two Swiss patent applications are pending, filed on 21 October 2025 and 19 September 2026. They will not be enforced offensively against good-faith users. See PATENT_PLEDGE.md for the binding defensive-only pledge, modeled on Tesla's 2014 EV patent pledge and the Open Invention Network.
Status
Pre-1.0. The API may change.
Apache 2.0 with a defensive-only patent pledge. Built in Switzerland.
Metadata
Release files for cage-ai 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cage_ai-0.1.1.tar.gz | 171.9 kB | With provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| cage_ai-0.1.1-cp314-cp314-manylinux_2_28_x86_64.whl | CPython 3.14 | CPython 3.14 | Linux glibc 2.28+ x86-64 | Details |
| cage_ai-0.1.1-cp314-cp314-macosx_11_0_arm64.whl | CPython 3.14 | CPython 3.14 | macOS 11.0+ ARM64 | Details |
| cage_ai-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.28+ x86-64 | Details |
| cage_ai-0.1.1-cp313-cp313-macosx_11_0_arm64.whl | CPython 3.13 | CPython 3.13 | macOS 11.0+ ARM64 | Details |
| cage_ai-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.28+ x86-64 | Details |
| cage_ai-0.1.1-cp312-cp312-macosx_11_0_arm64.whl | CPython 3.12 | CPython 3.12 | macOS 11.0+ ARM64 | Details |
| cage_ai-0.1.1-cp311-cp311-manylinux_2_28_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.28+ x86-64 | Details |
| cage_ai-0.1.1-cp311-cp311-macosx_11_0_arm64.whl | CPython 3.11 | CPython 3.11 | macOS 11.0+ ARM64 | Details |
Total release size: 9.1 MB
Release files / cage_ai-0.1.1.tar.gz
| Download URL | cage_ai-0.1.1.tar.gz |
|---|---|
| Size | 171.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fd24f3627d8288107cf57714b75428e7af115cfe8612ca419322cd714a0f4de8
|
|
BLAKE2b-256 checksum How to use checksums |
8144299226544618a7bbd533f0a35567c7ddf056cc7565f2915b8505be2f057d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp314-cp314-manylinux_2_28_x86_64.whl
| Download URL | cage_ai-0.1.1-cp314-cp314-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.14 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
840f17bf2096816b3552d8c1f33fbc7aff4926cf09b3bd49a7f0f2baadbcadb0
|
|
BLAKE2b-256 checksum How to use checksums |
3110e2af22e49a324552e09179175299573138d182d10aed640599233e79bd8e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp314-cp314-macosx_11_0_arm64.whl
| Download URL | cage_ai-0.1.1-cp314-cp314-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.14 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
450dd10208c8e0737c6e5314db5bb2ae43ae66e3d5b82a5dd70e7f7d4bf85d61
|
|
BLAKE2b-256 checksum How to use checksums |
72883487a37414e1ccaed0c4f5a85faed5fed3fc50ceb7f6b04fb23d8fc819dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl
| Download URL | cage_ai-0.1.1-cp313-cp313-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.13 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
7dd8baaa00bb220392bf0119e37a3757b17c4c264496887cb15e6007ab289758
|
|
BLAKE2b-256 checksum How to use checksums |
84947ebb859c69c739fab4012266df6ace3a5e62883bd037cc8fcdcc647e368e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp313-cp313-macosx_11_0_arm64.whl
| Download URL | cage_ai-0.1.1-cp313-cp313-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.13 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
ffaae8b0bf962b07dc4e1a9b58e2ef455b17518437f0d0d261c4f257ab2435a0
|
|
BLAKE2b-256 checksum How to use checksums |
676f03162f34a55cb7bd55bff1e1e4f77a62d12a56e8fa57fc63bbbb10460db3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl
| Download URL | cage_ai-0.1.1-cp312-cp312-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.12 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
ea5aa43aa1a5bd608f68500f86a9a43ba33b8220e9565b7dd58f0378bfb4fdc8
|
|
BLAKE2b-256 checksum How to use checksums |
dd23cf5deab98ee72849e309f747b2f1c05bd301668f7896b91c88e3d7bb5dd1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp312-cp312-macosx_11_0_arm64.whl
| Download URL | cage_ai-0.1.1-cp312-cp312-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.12 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
0d1db1400e7e782eec0d4511a81ad83b0cf02e7e192836877e1a0df9d47850f0
|
|
BLAKE2b-256 checksum How to use checksums |
1dc5a2703280d06971be1c816566cd53d51fffb4ba70bee1c4ba66d0b821b913
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp311-cp311-manylinux_2_28_x86_64.whl
| Download URL | cage_ai-0.1.1-cp311-cp311-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.11 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
0eddb9ef34eed110ddb3a6984004cd22f9d9faf0608fecb6c04ea1e29ae03139
|
|
BLAKE2b-256 checksum How to use checksums |
587ce8cb03f32bd7d29ddbca54c064b105ba61a2c2fa91039f13f088c82d03bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / cage_ai-0.1.1-cp311-cp311-macosx_11_0_arm64.whl
| Download URL | cage_ai-0.1.1-cp311-cp311-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.0 MB |
| Tags | CPython 3.11 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
a7c6b52d0c3ff9739d305c36d003fc7aee78ff65ec1cf823ff773223ea4fd9f8
|
|
BLAKE2b-256 checksum How to use checksums |
df48aab24178e467e9cfe9c92a70ec55aa3b0266b6a43d368b9865217c96f0fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logSecurity for cage-ai 0.1.1
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.
Full provenance
All 9 files in this release have been uploaded with provenance metadata.
Attestation for cage_ai-0.1.1.tar.gz
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 Oct 21, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.