Skip to content

Key Ideas at a Glance

This page condenses the core Caracal concepts into a quick reference. Read it as a map before diving into the detailed concept pages, or use it to orient yourself after the quickstart.

A zone is the top-level tenancy boundary. It owns:

  • Its own ES256 signing key (encrypted at rest with ZONE_KEK)
  • All applications, resources, providers, and policies within it
  • One active policy set at any time
  • A scoped audit ledger

Zones are independent. A mandate issued by one zone cannot be verified in another.

An application is a Caracal-registered client — a service, agent process, or automation that exchanges credentials for mandates. Applications belong to one zone and carry a credential_type (typically token or password) and a client secret.

A resource is anything an agent wants to access — an API endpoint, an MCP tool, a data store. Each resource has:

  • An identifier string (used in policy and grant definitions)
  • A list of scopes that gate access
  • An optional upstream_url (used by the Gateway for proxying)
  • An optional credential_provider_id (if the Gateway should substitute a provider token)

A policy is a versioned Rego document with the entry point data.caracal.authz.result. At token-exchange time, the STS evaluates the active policy set and passes a structured input:

{
"principal": { "type": "application", "id": "...", "zone_id": "...", "agent_session_id": "..." },
"resource": { "type": "resource", "id": "...", "identifier": "...", "scopes": ["..."] },
"action": { "id": "TokenExchange" },
"session": { "id": "..." },
"delegation_edge": { "id": "...", "constraints": { ... } },
"context": { "actor_claims": { ... }, "challenge_resolved": false }
}

The policy must return:

{
"decision": "allow",
"evaluation_status": "complete",
"determining_policies": ["..."],
"diagnostics": {}
}

decision must be "allow" and evaluation_status must be "complete" for the mandate to be issued. Any other result is a deny.

The following Rego builtins are blocked: http.send, net.*, opa.runtime, rand.intn, time.now_ns.

A mandate is an ES256-signed JWT issued by the STS after a policy allows the exchange. Two variants exist:

TypeTTLUse
Ambient60 minRe-presented to STS to obtain per-call tokens; carries agent identity
Per-call15 minPresented to Gateway or resources; scoped to specific targets

Per-call tokens carry a target claim (the specific resources they cover), preventing scope creep across resources.

JTI replay protection: each issued token ID is recorded in Redis. The Gateway rejects any per-call token whose JTI has been seen before.

Delegation is a directed edge in the Coordinator’s agent graph:

Application A (session S1) → delegates to → Application B (session S2)

Properties:

  • Cycle detection: the Coordinator rejects any delegation that would create a cycle
  • Depth limit: max 10 hops per chain; max 10 direct children per session; max 50 sessions per zone per app
  • Constraints: ttl_seconds, max_hops, budget (max scopes), policy_approved flag
  • Resource restriction: a delegation can be restricted to a single resource ID
  • Cascade revocation: revoking edge S1→S2 terminates S2 and all descendants of S2 recursively

When a delegated agent requests a mandate, the STS embeds the full delegation path in the JWT (delegation_chain claim). Resources that need to audit the agent lineage can inspect this chain.

Every token exchange and OPA evaluation produces an AuditEvent with:

  • decision (allow or deny)
  • evaluation_status and determining_policies
  • diagnostics from OPA
  • policy_set_version_id and manifest_sha (exactly which policy version decided)
  • A chain HMAC-SHA256 over consecutive events, enabling tamper detection

Events flow: STS → Redis stream (caracal.audit.events) → Audit service → PostgreSQL append-only table.

Nothing in the audit table is ever updated or deleted. Use caracal audit tail (live stream) or caracal explain <request-id> (full decision trace for a specific exchange) to query it.


Agent process
▼ (1) Exchange app credentials
STS (http://localhost:8080)
│ (2) Load active policy set for zone
│ (3) OPA evaluation per resource
│ → allow: issue mandate
│ → deny: 403 + audit event
▼ (4) Agent holds mandate (15-min per-call JWT)
Gateway (http://localhost:8081)
│ (5) Validate mandate signature (JWKS from STS)
│ (6) Check JTI replay cache
│ (7) Substitute provider credential if configured
▼ (8) Forward to upstream resource

Before an agent can get a mandate, these objects must exist in the control-plane API:

Zone → Application → Resource → Policy → Policy Set → activate → Grant

For local development, caracal init creates all of these automatically. For production, use the CLI admin commands or the @caracalai/admin SDK.


VariableUsed byRequired
CARACAL_ADMIN_TOKENCLI, admin SDKAdmin operations only
CARACAL_COORDINATOR_URLAll SDKsYes
CARACAL_ZONE_IDAll SDKsYes
CARACAL_APPLICATION_IDAll SDKsYes
CARACAL_SUBJECT_TOKENAll SDKsYes (injected by caracal run)
CARACAL_GATEWAY_URLAll SDKsWhen routing through Gateway
ZONE_KEKSTS, APIRequired at startup
AUDIT_HMAC_KEYSTS, AuditRequired at startup
STREAMS_HMAC_KEYAPI, Gateway, CoordinatorRequired at startup

ServicePortProtocol
API3000HTTP
STS8080HTTP
Gateway8081HTTP
Audit9090HTTP
Coordinator4000HTTP
PostgreSQL5432TCP
Redis6379TCP

In production, STS and Gateway require TLS. The CARACAL_ENV=dev flag relaxes TLS requirements for local development only.