---
title: Zone
description: The tenancy boundary that owns policies, keys, resources, and the audit ledger.
pageType: concept
keywords: [zone, tenancy, signing key, DEK, KEK, JWKS, ChaCha20, isolation, multi-tenant]
---

A zone is the top-level organizational boundary in Caracal. Everything — applications, resources, policies, grants, sessions, and audit events — belongs to exactly one zone. Zones are independent: a mandate issued in one zone cannot be verified in another, and a policy in one zone has no effect on another.

## What a zone contains

| Item | Scoped to zone |
| ---- | -------------- |
| Applications | Yes — each application belongs to one zone |
| Resources and providers | Yes |
| Policies and policy sets | Yes — one active policy set per zone at a time |
| Grants | Yes |
| Agent sessions | Yes |
| Audit events | Yes |
| Signing key pair | Yes — each zone has its own EC P-256 key pair |

## Zone attributes

A zone is stored with these attributes:

| Field | Description |
| ----- | ----------- |
| `id` | UUID; carried in every mandate as `zone_id` |
| `org_id` | Organization identifier (default: `"default"`) |
| `name` | Human-readable label |
| `slug` | URL-safe identifier (matches `/^[a-z0-9-]+$/`) |
| `dek_ciphertext` | The zone's data encryption key, encrypted under the KEK |
| `dcr_enabled` | Whether Dynamic Client Registration is permitted |
| `pkce_required` | Whether PKCE is required for authorization code flows |
| `login_flow` | Authentication flow type for this zone |

## The key hierarchy

Each zone has its own signing key pair used to sign and verify mandates. This isolation means a compromised key in one zone does not affect others, and keys can be rotated per zone independently.

The key hierarchy has two levels:

```
ZONE_KEK (environment variable, 32 bytes)
    │
    └─ encrypts → zone Data Encryption Key (DEK, stored as dek_ciphertext in DB)
                      │
                      └─ encrypts → zone signing key (EC P-256 private key, stored in secrets table)
```

**ZONE_KEK** is a 32-byte hex-encoded key set in the `ZONE_KEK` environment variable on the STS. It is the root of the key hierarchy. The STS validates on startup that `ZONE_KEK` is present, is 32 bytes, and is not all zeros.

**Zone DEK** (`dek_ciphertext`) is stored in the `zones` table. The DEK is decrypted by the STS using ChaCha20-Poly1305 with the `ZONE_KEK`. The decrypted DEK is then used to decrypt the zone's signing key from the `secrets` table.

**Zone signing key** is the EC P-256 private key used to sign mandates. It is stored encrypted under the zone DEK, never in plaintext at rest.

The `ZONE_KEK_PROVIDER` environment variable controls where the KEK is sourced. The current supported value is `"local"` (reads from the `ZONE_KEK` env var). Future values may support external KMS providers.

## JWKS endpoint

The STS exposes the zone's public key(s) for verification:

```
GET /zones/{zoneId}/.well-known/jwks.json
```

This endpoint returns up to two recent public keys to support key rotation without breaking in-flight tokens that were signed with the previous key. JWKS responses are cached by the Gateway and SDK connectors, refreshed every 5 minutes.

## One active policy set per zone

A zone has exactly one active policy set binding at a time. All token exchanges in the zone are evaluated against the active bundle. A zone with no active binding evaluates with the deny-all fallback.

Policy set activations are propagated to the STS immediately through the `caracal.policy.invalidate` Redis stream, which triggers a bundle reload for the zone.

## Zone isolation in practice

Two zones running on the same stack are as independent as two separate deployments. They share PostgreSQL and Redis infrastructure but maintain separate:

- Signing keys (mandates can only be verified by the issuing zone's JWKS)
- Policy sets (an activation in zone A does not affect zone B)
- Session namespaces (agent sessions are keyed by `zone_id`)
- Audit ledgers (audit events carry `zone_id` and are queryable per zone)

A mandate issued by zone A carries `zone_id = "zone-A"` in its claims. The Gateway verifies the token against the JWKS of the zone specified in the token — a token for zone A cannot be verified against zone B's JWKS.

## When to use multiple zones

Use separate zones when you need:

- Different policy sets for different environments (staging vs production)
- Different signing keys for different teams or services
- Audit segregation by organizational unit
- Independent key rotation schedules

A single zone is sufficient for most deployments where all services share the same policies and trust boundary.

## Next steps

- [Resource and Grant](./resource-grant/) — resources and grants within a zone
- [Policy](./policy/) — activating a policy set in a zone
- [Mandate](./mandate/) — how zone signing keys produce mandates
