What Caracal Does
Caracal enforces authorization before an agent acts, not after. This page explains how that enforcement works, what it produces, and how it differs from conventional middleware approaches.
The core problem
Section titled “The core problem”AI agents and autonomous workflows need access to tools, APIs, and data. Giving them long-lived credentials causes two problems: you cannot know what the agent will do with them before it acts, and you cannot revoke access granularly once something goes wrong.
Caracal addresses both problems by issuing a mandate — a short-lived, scoped JWT — only after an OPA policy evaluation determines the requested access is allowed. The agent receives a credential only if the policy explicitly permits it, and that credential expires in minutes, not days.
The enforcement point
Section titled “The enforcement point”The Security Token Service (STS) is the enforcement hub. Every agent that wants to call a tool or API must first exchange credentials with the STS for a mandate. The STS does this before issuing any token:
- Receives an exchange request carrying the agent’s application credentials and a list of resources it wants to access.
- Loads the active policy set for the zone the agent belongs to.
- Calls OPA with a structured input containing the principal, resource, action, session state, and delegation chain.
- Only if OPA returns
decision = "allow"withevaluation_status = "complete"does the STS sign and return a mandate. - Emits an audit event for every resource evaluation, whether allowed or denied.
If no policy set is active in the zone, the STS installs a deny-all fallback — no agent can exchange credentials until a policy is activated. There is no default-allow state.
What a mandate carries
Section titled “What a mandate carries”A mandate is an ES256-signed JWT. The claims it carries go beyond standard OAuth tokens:
| Claim | Purpose |
|---|---|
sub | Application identity of the agent |
zone_id | Zone this token is bound to |
scope | OAuth scopes the agent was granted |
target | Array of resource identifiers the token is valid for |
sid | Session ID binding the token to a specific agent session |
agent_session_id | ID of the agent session in the Coordinator |
delegation_edge_id | ID of the delegation edge, if this agent was delegated to |
delegation_chain | Full chain of sessions from root to this agent |
hop_count | Number of delegation hops |
exp | Expiry (15 minutes for per-call tokens, 60 minutes for ambient tokens) |
jti | Unique token ID for replay protection |
Two token types exist:
- Ambient tokens (
use = "ambient") are issued when an agent starts a session. They carry the agent’s identity and can be re-presented to the STS to exchange for narrower per-call tokens bound to a specific resource. TTL: 60 minutes. - Per-call tokens (
use = "per-call") are the tokens an agent presents to the Gateway or a resource. They are scoped to specific resources and cannot be re-used as a subject token for further exchange. TTL: 15 minutes.
The Gateway
Section titled “The Gateway”When an agent calls an MCP tool or upstream API, the request passes through the Gateway. The Gateway:
- Validates the mandate’s signature against the zone’s JWKS endpoint.
- Checks the JTI for replay (each JTI is accepted only once).
- Confirms the resource being accessed matches a binding registered for that upstream.
- Optionally substitutes a provider OAuth token or API key for the upstream call — the agent never holds those credentials directly.
The agent presents its mandate; the Gateway handles the actual credential presented to the upstream. This is credential brokering: agents hold short-lived mandates, not provider secrets.
Delegation
Section titled “Delegation”An agent can delegate a subset of its authority to another agent by creating a delegation edge in the Coordinator. Delegation is:
- Graph-tracked: edges are stored and traversed in the database; cycle detection prevents loops
- Caveat-constrained: edges carry TTL, hop count, budget, and scope limits
- Cascading on revocation: revoking an edge terminates all downstream edges and agent sessions in a single operation
When a delegated agent exchanges for a mandate, the STS validates the full delegation chain and embeds it in the JWT. A resource can inspect the chain to understand the full lineage of the agent that called it.
The audit ledger
Section titled “The audit ledger”Every token exchange and OPA decision produces an AuditEvent emitted to a Redis stream (caracal.audit.events). The Audit service consumes this stream and writes events to a PostgreSQL append-only table. Events include:
- The OPA decision (
allowordeny) - The evaluation status and determining policies
- The OPA diagnostic output
- The policy set version and manifest SHA that produced the decision
- A chain HMAC over consecutive events, enabling tampering detection
Nothing is ever updated or deleted in the audit table. Querying the audit trail is available through the TUI and CLI (audit tail, explain).
What this is not
Section titled “What this is not”Caracal does not replace your LLM framework, agent scheduler, or prompt router. It sits alongside those systems as an authorization layer. An agent calls Caracal’s SDK to get a mandate, and then uses that mandate when calling tools through the Gateway or directly to resources that verify it. Caracal does not inspect or filter the content of tool calls — it enforces whether the call is permitted to happen at all.
Next step
Section titled “Next step”Read Installation to install the CLI and bring up the stack, or skip to Key Ideas at a Glance for a condensed reference of the full model.