Skip to content

What Zones Are

A zone is a named compartment binding a policy to an identity. A run starts in a low-privilege zone with no credentials and escalates into a higher one for individual commands, each checked separately.

Zones are optional. With no zones.toml in the machine config directory (/etc/wardcell, beside config.toml) they do not exist, and wardcell works exactly as the rest of these docs describe — one policy per run, selected by --profile or selects_on. You never need a zone to sandbox an agent.

Add them when you want granular, identity-aware control over what an agent may do as you. An agent needs a credential for a fraction of its work — pushing a branch, calling an internal API — but without zones it either holds that credential for all of its work or cannot do that part at all.

The common shape is two zones:

  • An anonymous zone with no credential injection — the agent can read the project and reach the open web, which is all it needs to research, search, and draft. Nothing it does there is signed as you.
  • A user zone it escalates into only for the steps that act as you — git push, calling a real API — where the proxy attaches your identity to the request. The agent never holds the secret; it just asked to enter the zone, and that request was checked.

So the agent roams freely while it is only reading and thinking — exactly where a prompt injection would land — and your identity is spent only on the specific commands that genuinely need it.

Entering a zone can also require a human to approve it, gated on the command the escalation is trying to run: git push into the user zone can be waved through while curl into it waits for you to say yes. The agent asks to enter, you see what it wants to run and from where, and the step proceeds only once you approve — so a hijacked agent cannot spend your identity without a person in the loop. See Entry Approval.

flowchart LR
  subgraph host["host (trusted)"]
    S["supervisor"]
  end
  subgraph z1["zone: anonymous"]
    A["agent<br/>no credentials"]
  end
  subgraph z2["zone: user"]
    B["git push<br/>credential injected at the proxy"]
  end
  A -->|"zone enter user"| S
  S -->|"checks enter_from,<br/>spawns a new sandbox"| B

The policy half is nothing new — it is ordinary fragment resolution against the same base.sbx, the same selects_on, the same merge algorithm. A zone’s filesystem and network reach is exactly what --profile <that profile> would have produced.

Only the identity half is new: whether the proxy attaches a credential to outbound requests, and which plugin decides what that credential is.

There is no second merge algorithm and no per-zone base policy.

No zones.toml in /etc/wardcellzones do not exist. --profile works as normal. Nothing about using wardcell requires this file.
zones.toml presentevery run and draft resolves through a zone, and --profile is rejected.

A fresh install starts in the first state.

wardcell zone enter never widens the calling process. It asks a supervisor — a trusted process outside the sandbox — which spawns a brand-new sandbox for the target zone and runs the command there.

That matters because a self-elevation path is one bug away from being an escape. A supervisor that only ever constructs fresh sandboxes has no in-process state an attacker can corrupt into a wider grant.

Zone identity is also a property of which loopback port a connection arrived on — each zone gets its own proxy listener — never something inferred from request content. A forged header cannot claim a zone.

Yes, if a credential should exist for some commands and not others, or if an org wants to mandate a privilege floor across a fleet.

No, if one policy covers the whole run. A plain profile is simpler and does the same job.