System Topology
Caracal runs as seven containers: five application services, one Postgres instance, and one Redis instance. An eighth init container runs once at startup to provision Redis streams and then exits. All ports are bound to localhost in development; production deployments place services behind network policies appropriate to the deployment target.
Services
Section titled “Services”STS — Security Token Service
Section titled “STS — Security Token Service”| Attribute | Value |
|---|---|
| Language | Go |
| Port | 8080 |
| Image | caracal/sts:dev |
The STS is the sole issuer of mandates. It receives RFC 8693 token exchange requests, evaluates the active OPA policy for each requested resource, signs a JWT mandate for resources that are allowed, and emits an audit event for every evaluation. No mandate can be issued without passing through the STS.
Endpoints:
| Path | Purpose |
|---|---|
POST /oauth/2/token | RFC 8693 token exchange |
GET /.well-known/jwks.json?zone_id= | Zone-specific public key set |
GET /step-up/{id} | Step-up challenge status query |
GET /health | Liveness probe (always 200) |
GET /ready | Startup probe (Postgres + Redis) |
GET /metrics | JSON metrics snapshot |
Connections: Postgres (read sessions, applications, resources, policies, grants; write sessions, step-up challenges), Redis (consume: caracal.sessions.revoke, caracal.policy.invalidate, caracal.keys.invalidate; publish: caracal.audit.events).
Gateway — MCP Reverse Proxy
Section titled “Gateway — MCP Reverse Proxy”| Attribute | Value |
|---|---|
| Language | Go |
| Port | 8081 |
| Image | caracal/gateway:dev |
The Gateway is the ingress point for all agent requests. On each inbound request it exchanges the agent’s ambient mandate with the STS for a per-call token, verifies the response, checks the JTI replay cache, and forwards the request to the upstream with the appropriate authorization header. The upstream never sees the agent’s ambient mandate.
Endpoints:
| Path | Purpose |
|---|---|
GET /health | Liveness probe |
GET /ready | Startup probe (Postgres, Redis, STS) |
GET /metrics | JSON metrics |
/* | Reverse proxy handler |
Connections: Postgres (SELECT: zones, resources, providers, gateway_resource_bindings), Redis (JTI deduplication, stream signature verification, caracal.sessions.revoke via gateway-revocation), STS (JWKS fetch, per-call token exchange).
API — Control Plane
Section titled “API — Control Plane”| Attribute | Value |
|---|---|
| Language | TypeScript / Fastify |
| Port | 3000 |
| Image | caracal/api:dev |
The API is the admin control plane. It manages zones, applications, resources, credential providers, policies, policy sets, grants, invitations, and teams. All management operations go through the API. It also runs background workers for outbox publishing, session reaping, and DCR garbage collection.
Key endpoints (v1): Zones, applications, resources, providers, policies, policy sets, grants, invitations, teams, step-up challenges, policy templates, zone events, and a loopback-only bootstrap route when CARACAL_LOCAL_BOOTSTRAP_ENABLED=true.
Connections: Postgres (read/write: all control-plane tables), Redis (outbox stream publishing).
Coordinator — Agent Session Manager
Section titled “Coordinator — Agent Session Manager”| Attribute | Value |
|---|---|
| Language | TypeScript / Fastify |
| Port | 4000 |
| Image | caracal/coordinator:dev |
The Coordinator manages agent session lifecycle and the delegation graph. It exposes a flat-HTTP V1 façade used by the SDKs, and richer internal routes for direct graph access. It runs TTL sweepers, deadline enforcers, and a retention cleaner. A sidecar relay process consumes caracal.agents.lifecycle events.
V1 façade:
| Route | Purpose |
|---|---|
POST /v1/begin | Open an agent session |
POST /v1/end | Close a session and cascade-terminate descendants |
POST /v1/exchange | Create a delegation edge between two agent sessions |
POST /v1/verify | Verify a bearer mandate (rate-limited: 60/min per IP) |
Connections: Postgres (read/write: agent_sessions, delegation_edges, agent_invocations, agent_services, caracal_outbox, delegation_graph_epochs), Redis (outbox stream publishing, caracal.agents.lifecycle consumption), STS (bearer token verification).
Audit — Event Ingestor
Section titled “Audit — Event Ingestor”| Attribute | Value |
|---|---|
| Language | Go |
| Port | 9090 |
| Image | caracal/audit:dev |
The Audit service consumes caracal.audit.events and writes HMAC-chained records into the append-only audit_events table. It runs tamper detection (startup full-chain sweep and hourly incremental check), drives optional Parquet export to S3, and enforces the retention policy. Leader election via Postgres advisory lock prevents multiple instances from running the exporter or retention job simultaneously.
Connections: Postgres (INSERT + SELECT: audit_events, audit_export_watermark, audit_ingest_alerts), Redis (consume caracal.audit.events via audit-ingestor and siem-export).
Infrastructure
Section titled “Infrastructure”PostgreSQL
Section titled “PostgreSQL”| Attribute | Value |
|---|---|
| Port | 5432 |
| Volume | postgresData:/var/lib/postgresql |
| Healthcheck | pg_isready (5 s interval, 20 retries) |
Shared by all five services. Each service connects with a dedicated database role scoped to the tables and operations it requires. See Storage Model for the full schema and role permissions.
| Attribute | Value |
|---|---|
| Port | 6379 |
| Auth | Required (REDIS_PASSWORD) |
| Volume | redisData:/data |
| Healthcheck | redis-cli PING (5 s interval, 20 retries) |
Shared by all five services for streams, the JTI deduplication cache, and outbox publishing. All messages written to streams include an HMAC-SHA256 signature under STREAMS_HMAC_KEY. See Event Streams and Outbox for the full stream inventory.
Init container
Section titled “Init container”Runs provision-streams.sh once. Creates all nine Redis consumer groups with XGROUP CREATE … MKSTREAM. Exits after completion and does not restart. Both Postgres and Redis must be healthy before the init container runs.
Startup dependency order
Section titled “Startup dependency order”postgres ──────────────────────────────────────┐ ▼redis ──────────────────────────────────────► init (exits) │ ┌────────────────┤ ▼ ▼ STS Audit │ ┌────┤ ▼ ▼ API Coordinator │ ▼ GatewayEach service waits for a healthy signal from its dependencies before starting. Gateway waits for both API and STS healthy. Coordinator waits for STS healthy. API waits for STS healthy. STS and Audit wait for init to complete successfully.
Connection matrix
Section titled “Connection matrix”| Postgres | Redis | STS | API | Gateway | Audit | Coordinator | |
|---|---|---|---|---|---|---|---|
| STS | ✓ | ✓ | — | — | — | — | — |
| Gateway | ✓ | ✓ | ✓ | — | — | — | — |
| API | ✓ | ✓ | — | — | — | — | — |
| Audit | ✓ | ✓ | — | — | — | — | — |
| Coordinator | ✓ | ✓ | ✓ | — | — | — | — |
| CLI/SDK | — | — | ✓ | ✓ | ✓ | — | ✓ |
No service makes direct HTTP calls to another service except where the matrix shows ✓. The Gateway calls STS for every inbound request. The Coordinator calls STS only for bearer verification. The API publishes to Redis streams via the outbox; it does not call any other service directly.
Port reservation
Section titled “Port reservation”These localhost ports are reserved for the OSS stack. They must not be reused by the enterprise stack or other co-located services.
| Port | Service |
|---|---|
| 3000 | API |
| 4000 | Coordinator |
| 5432 | PostgreSQL |
| 6379 | Redis |
| 8080 | STS |
| 8081 | Gateway |
| 9090 | Audit |