Security Zones
Zones classify targets — the MCP servers an agent can reach — so that crossing a perimeter becomes something you can scope, escalate on, and reconstruct afterwards.
The problem they solve is the one workflow that touches three vendors and leaves no story behind it. A session that reads customer records and then posts externally is both an ordinary support workflow and the exact shape of exfiltration. Zones make the difference legible.
There is no tool-level metadata table to hang a zone on, so v1 resolves at server granularity. Finer grain is a later sprint, not a missing piece of this one.
The shipped zones
Five ship by default, and the set is tenant-extensible:
| Zone | Typical contents |
|---|---|
public | Documentation, public APIs, search |
internal | Ordinary internal systems |
pii_sensitive | Customer records, HR, anything personal |
infrastructure | Deployment, cloud control planes, CI |
external_write | Email, ticketing, anything that emits outside the org |
Inference proposes, an admin promotes
This is the precision rule expressed as schema rather than as a feature flag — and it is why zones need no opt-in switch.
Every target lands at zone_state = 'inferred', assigned by name-and-URL heuristics at registration. An inferred zone is record-only by construction. It emits ZONE_SUGGESTED and allows the call, no matter how alarming the inference looks.
Only an admin promoting a target to confirmed makes it deny inline.
An imprecise detector records and lets an admin follow up. A deterministic one — an admin's own classification — may deny. A deployment that classifies nothing behaves exactly as it did before zones existed.
Cross-zone reach folds into blast radius
Cross-zone reach is a blast-radius question — how far can this one call travel — so zone rules became a single new branch inside the existing blast-radius check rather than a sixteenth sequential gate in the hot path.
The zone logic is pure and synchronous, with no database, agent, or session access: the engine resolves a decision from rows it already holds and passes that decision in. Omitting it reproduces the prior behaviour exactly.
The exfiltration shape
A sensitive cross-zone transition inside a single session — pii_sensitive followed by external_write — runs the zone_transitions policy and emits a ZONE_TRANSITION event carrying the session's full zone path. The crossing is reconstructable from the record, not merely alerted on at the moment it happened.
Not just the immediately preceding one. Otherwise reading customer records, then a public docs page, then posting externally would launder the crossing.
Seeded to escalate, not to deny
"Read customer records, then send an email" is both the exfiltration signature and a support workflow that runs a hundred times a day. Denying it outright breaks real work, and a control that breaks real work gets switched off.
So the seeded pairs escalate: a human is put on the one decision that matters, and the transition is recorded either way.
Direction matters, and the pairs are ordered. public → pii_sensitive is ordinary. pii_sensitive → public is not.
The session zone path is durable
The path lives on the session record (sessions.zones_touched), not in process memory. The path is the evidence — an in-memory set would work on one box and fail silently across a fleet.
It advances only on permitted calls. A denied call never reached the zone, and recording it would corrupt the path that every later transition is judged against.
Scoping an agent to zones
allowed_zones on the agent record, and in the agent JWT.
A NULL value reads as the default internal grant. That is what lets a newly-created agent be scoped without anyone writing a value — see one-line onboarding.
Where zones meet autonomy
The infrastructure zone requires autonomy level A2 or above, and that requirement is enforced only when the tenant has enabled autonomy enforcement. Zones inherit that opt-in rather than introducing a second switch.
Zone breadth and sensitivity also feed the BRF Access dimension. Holding pii_sensitive and external_write together scores above the sum of its parts, because that combination is the lethal-trifecta shape.
API
| Endpoint | Purpose |
|---|---|
GET /api/v1/policy/zones | List zones for the tenant |
GET / PUT /api/v1/targets/{id}/zone | Read or set a target's zone and state |
GET / PUT /api/v1/agents/{id}/zones | Read or set an agent's allowed_zones |
GET / PUT /api/v1/policy/zone-transitions | The transition policy |
The dashboard ships a zone grant editor and a zone-path renderer on the session view.
Upgrading
Nothing enforces on upgrade. Every target arrives inferred, which records and prompts but never blocks. The first denial requires an admin to have looked at a target and confirmed its zone.
There is a schema change on both planes, so a data-plane upgrade runs alembic upgrade dp@head and a control-plane upgrade runs alembic upgrade cp@head.
Related
- Autonomy Levels — the granted-authority axis zones interact with
- Blast radius — the gate zone rules fold into
- Decision Trace — where the zone path is recorded