Draft WARDEN-WP-0032 properly — security zones, ownership question first
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Expands the placeholder into a real plan, grounded in what the estate already
has rather than first principles:

- tenancy-posture_v0.1 is the structural model to follow (canon standard +
  per-repo declaration + "accuracy, not altitude"), and its §14 adoption stance
  is "structure, not tooling" — a precedent about sequencing.
- Reefs are substrate placement zones with real risk statements. A security
  zone is not a reef; repo-manager's own "topology is not readiness" applies
  here as "placement is not posture".
- flex-auth is latency-critical and already consumes a compiled registry
  snapshot, so zone membership should reach the PDP by compilation rather than
  a synchronous lookup in the decision path.
- warden plan already returns verdicts with reasons; zone-aware enforcement
  extends that machinery instead of growing a parallel one.

T01 settles ownership before any modelling, because it decides which repo the
rest lives in: canon standard, a zone-engine runtime authority, or flex-auth
policy data. The recommendation is canon-first with zone-engine named as an
explicit promotion — but the time-boxed refactor exception is the case that
could justify an engine immediately, since an expiring grant is state and state
wants an owner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-19 20:38:25 +02:00
parent 3d968bb993
commit b845f4b51e
2 changed files with 180 additions and 63 deletions

View file

@ -27,7 +27,8 @@
| workplan | WARDEN-WP-0028 | finished | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md | | workplan | WARDEN-WP-0028 | finished | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| workplan | WARDEN-WP-0029 | finished | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md | | workplan | WARDEN-WP-0029 | finished | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| workplan | WARDEN-WP-0030 | finished | — | workplans/WARDEN-WP-0030-delegation-register.md | | workplan | WARDEN-WP-0030 | finished | — | workplans/WARDEN-WP-0030-delegation-register.md |
| workplan | WARDEN-WP-0031 | active | — | workplans/WARDEN-WP-0031-policy-caller-identity.md | | workplan | WARDEN-WP-0031 | finished | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| workplan | WARDEN-WP-0032 | proposed | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | ADHOC-2026-06-27-T01 | done | — | workplans/ADHOC-2026-06-27.md | | task | ADHOC-2026-06-27-T01 | done | — | workplans/ADHOC-2026-06-27.md |
| task | ADHOC-2026-06-29-T01 | done | — | workplans/ADHOC-2026-06-29.md | | task | ADHOC-2026-06-29-T01 | done | — | workplans/ADHOC-2026-06-29.md |
| task | ADHOC-2026-08-11-T01 | done | — | workplans/ADHOC-2026-08-11.md | | task | ADHOC-2026-08-11-T01 | done | — | workplans/ADHOC-2026-08-11.md |
@ -113,4 +114,9 @@
| task | WARDEN-WP-0031-T02 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md | | task | WARDEN-WP-0031-T02 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T03 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md | | task | WARDEN-WP-0031-T03 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T04 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md | | task | WARDEN-WP-0031-T04 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T05 | wait | — | workplans/WARDEN-WP-0031-policy-caller-identity.md | | task | WARDEN-WP-0031-T05 | cancel | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0032-T01 | todo | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T02 | todo | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T03 | todo | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T04 | todo | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T05 | todo | — | workplans/WARDEN-WP-0032-security-zones.md |

View file

@ -1,7 +1,7 @@
--- ---
id: WARDEN-WP-0032 id: WARDEN-WP-0032
type: workplan type: workplan
title: "Security zones — scope enforcement to bands of differing rigidity" title: "Security zones — bands of enforcement rigidity, and where they live"
domain: infotech domain: infotech
repo: ops-warden repo: ops-warden
status: proposed status: proposed
@ -18,48 +18,103 @@ updated: "2026-08-19"
# WARDEN-WP-0032 — Security zones # WARDEN-WP-0032 — Security zones
`ADR-0006` defers `policy.enabled: true` until enforcement can be scoped. This `ADR-0006` defers `policy.enabled: true` until enforcement can be scoped to a
workplan builds the thing it defers to. zone. This workplan builds the model it defers to, and — first — settles who
owns it.
## The problem in one paragraph ## The problem, precisely
ops-warden has three posture axes already: environment (`dev`/`test`/`prod`, ops-warden can express *how exposed* something is and *how mature* it is. It
WP-0015), workload maturity (`M0``M3`, WP-0015), and organization posture cannot express *how rigidly this band of the estate is governed right now*.
(`build`, WP-0029). What it does not have is a way to say *"this band of the
estate is enforced, that one is advisory"* — so every enforcement control it
ships is a repo-wide boolean. `policy.enabled` is the first one to actually
become flippable, and flipping it would harden the access needed to perform the
refactors currently underway. The axes describe posture; nothing acts on them.
## What a zone has to answer | Axis | Shipped | Answers |
| --- | --- | --- |
| Environment posture (`dev`/`test`/`prod`) | WP-0015 | Where does this run? |
| Workload maturity (`M0``M3`) | WP-0015 | How ready is this workload? |
| `organization_posture: build` | WP-0029 | What state is the org in? |
| **Enforcement stance** | **missing** | **Is this control enforced here, and what happens when it fails?** |
A zone is only useful if it decides something. Minimum: Because the fourth is missing, every enforcement control ops-warden ships is a
repo-wide boolean. `policy.enabled` is the first to become flippable, and
flipping it would make flex-auth a hard dependency of every `warden sign`
including the certificates the ops-bridge tunnels depend on, one of which
carries the policy call itself. Across an estate mid-refactor that hardens
exactly the access needed to perform the refactor.
- **Membership** — what puts a lane, actor, or workload in this zone? Derived The three existing axes *describe*. Nothing *decides*. A zone is the object that
from the existing axes where possible rather than a fourth hand-maintained decides.
list.
- **Enforcement stance per control** — for each gate (pre-sign policy, agent
read-boundary, high-risk refusal): enforced, advisory, or exempt.
- **Failure mode** — fail-closed or fail-open, per control per zone. This is the
actual bite: `fail_closed` is what makes a dead tunnel an outage.
- **Who may change it, and how it is observed** — a zone that can be quietly
widened is not a boundary.
## Open questions to settle before designing ## What already exists — do not reinvent these
- Is a zone a property of the **actor** (`adm`/`agt`/`atm`), the **lane** Four estate mechanisms are adjacent, and getting the relationships right is most
(catalog entry), the **workload**, or the **moment** (an active refactor)? The of the design work.
CoulombCore decommission suggests the last one matters and is the hardest to
model — a time-boxed relaxation is a different object than a standing zone. **`net-kingdom/canon/standards/tenancy-posture_v0.1.md`** — the closest
- Do zones compose with the three existing axes, or replace one? A fourth structural analogue and the model to follow: six orthogonal axes, graduated
independent axis multiplies states; folding `organization_posture` in may be levels, a per-repo declaration file (`tenancy.yaml`) carrying current /
the honest move. implemented / target vectors with evidence and a review date, and §6
- Is this ops-warden's model to own at all, or NetKingdom canon that ops-warden "conformance is accuracy, not altitude". Its §14 adoption stance is
consumes? ADR-0005 says implement narrowly and route broadly. A security zone *"structure, not tooling"* — a direct precedent about sequencing, and one this
model that only ops-warden honours is worth much less than one flex-auth, workplan should not quietly break.
secrets-engine, and tenant-engine also read — but ops-warden should not
absorb an estate-wide model on its own authority either. **Settle this **Reefs** (`repo-manager/docs/RailianceAppDeploymentGuide.md`) — `reef-railiance`,
first**; it changes who writes the rest. `reef-storage`, named in a rapp's `bound_reefs`. These are **substrate**
placement zones with real risk statements attached (`reef-railiance` carries a
single-node/shared-control-plane residual risk requiring explicit acceptance).
A security zone is *not* a reef — reef answers *where does it run*, zone answers
*how rigidly is it governed* — but they are correlated, and a taxonomy that
ignores reefs will fight them. Also note repo-manager's own ADR-0006: *topology
is not readiness*. The same caution applies here: **placement is not posture.**
**flex-auth** — the PDP, `service_class: latency-critical`, which already
consumes a *compiled registry snapshot*
(`registry/flex-auth/production_registry_snapshot.json`, built by
`scripts/build_flex_auth_registry.py`). This matters enormously for T03: zone
membership can reach the decision point by compilation, with no runtime lookup
and no new service in the request path.
**`warden plan`** (WP-0029) — already classifies a need as `autonomous` /
`founder_required` / `unroutable` from properties of the thing, returning the
`reasons` that produced the verdict. Zone-aware enforcement should extend this
verdict machinery rather than grow a parallel one.
## The question that changes everything else: who owns this?
Three placements are defensible. **T01 settles it before any modelling**,
because the answer changes who writes T02T05 and in which repo they live.
**(a) NetKingdom canon + per-repo declaration.** A `security-zones_v0.1.md`
standard beside `tenancy-posture_v0.1.md`; each repo declares its lanes'
zones in a `zones.yaml` the way it already declares `tenancy.yaml`; flex-auth
enforces from the compiled registry. ops-warden drafts and offers, exactly as it
did for the WP-0015 maturity model. *Strongest fit to precedent; no new runtime
dependency; matches "structure, not tooling".*
**(b) A new `zone-engine` repo.** A runtime authority owning zone identity,
membership, and lifecycle, in the family of `tenant-engine` / `user-engine` /
`secrets-engine` — each of which has a boundary contract in canon. *Justified
if and only if zone membership needs minting, an API other components query, or
— the real candidate — **time-boxed exceptions with expiry and an audit trail of
who widened what**. A standing classification does not need an engine; a
relaxation that must expire probably does.*
**(c) flex-auth policy data.** Zones as attributes in the policy package, no new
artefact anywhere. *Cheapest, and wrong if zones must govern controls flex-auth
does not evaluate — the agent read-boundary (ADR-0004) and `warden desk`
escalation are ops-warden-side and would fall outside.*
**Recommendation to be tested in T01, not assumed:** start at (a), and name
(b) as an explicit promotion with a written trigger, rather than seeding an
engine before the model exists. An empty `zone-engine` would end up defining
zones by accident — through whatever its first schema happened to be — which is
the failure `ADR-0001` names in a different register. The counter-argument
deserves a fair hearing: if time-boxed refactor exceptions are in scope from day
one, (b) may be right immediately, because an expiring grant is state and state
wants an owner.
Under `ADR-0005` ops-warden does not absorb an estate-wide model on its own
authority. A zone model only ops-warden honours is worth much less than one
flex-auth enforces and secrets-engine reads.
## Tasks ## Tasks
@ -69,11 +124,15 @@ status: todo
priority: high priority: high
``` ```
**Decide ownership before designing.** Take the third open question to **Settle ownership and placement.** Take the three options above to
net-kingdom (canon) and flex-auth (the PDP that would enforce it). If it is `net-kingdom` (canon) and `flex-auth` (the PDP that would enforce), with the
canon, ops-warden drafts and offers, as it did for the WP-0015 maturity model, time-boxed-exception question stated explicitly since it is what decides
and consumes the result. If it is ops-warden's, say so explicitly and record between (a) and (b). Record the outcome as a decision with its reasoning — if
why. Do not build a model whose owner is unsettled. it lands outside ops-warden, this workplan's remaining tasks move to the owning
repo and ops-warden keeps only its consumer-side tasks.
Deliverable: a decision record, and coordination messages sent. Do not model
anything before this closes.
```task ```task
id: WARDEN-WP-0032-T02 id: WARDEN-WP-0032-T02
@ -81,12 +140,24 @@ status: todo
priority: high priority: high
``` ```
**Name the zones against reality, not in the abstract.** Walk the current **Derive the zones from the real estate, not from first principles.** Walk the
estate — the 27 catalog lanes, the actor inventory, the workloads with posture 27 catalog lanes, the actor inventory (`adm`/`agt`/`atm`), and the workloads
descriptors — and see what bands actually exist. A zone model that does not carrying posture descriptors, and find the bands that actually exist. The test
partition today's lanes cleanly is wrong, and the exceptions are the interesting of the model is that it partitions today's estate without a residue of
part. Include the refactor case explicitly: what zone is a lane in while the exceptions; the exceptions found are the most informative output.
component it fronts is being rebuilt?
Must answer explicitly:
- Is a zone a property of the **actor**, the **lane**, the **workload**, or the
**moment**? The CoulombCore decommission shows the last one is real and is the
hardest to model — a time-boxed relaxation is a different object from a
standing zone, and conflating them produces a permanent hole with a
temporary-sounding name.
- Do zones **compose with or fold in** the three existing axes? A fourth
independent axis multiplies states; absorbing `organization_posture` may be
the honest move.
- How do zones relate to **reefs**? Correlated, not identical. Say so in the
model rather than leaving readers to assume either.
```task ```task
id: WARDEN-WP-0032-T03 id: WARDEN-WP-0032-T03
@ -94,22 +165,35 @@ status: todo
priority: high priority: high
``` ```
**Model enforcement stance and failure mode per zone per control,** covering at **Model stance and failure mode per zone per control.** For each control:
minimum: the flex-auth pre-sign gate (`policy.enabled` / `fail_closed`), the enforced / advisory / exempt, *and* fail-closed or fail-open. The failure mode
agent read-boundary on `risk: high` lanes (ADR-0004), and the `warden plan` is the part with teeth — `fail_closed` is what turns a dead tunnel into an
escalation verdicts (WP-0029). Each must be expressible as enforced / advisory / outage, and it is the reason `policy.enabled` was deferred.
exempt with an explicit failure mode.
Controls in scope at minimum: the flex-auth pre-sign gate (`policy.enabled`,
`fail_closed`), the agent read-boundary on `risk: high` lanes (`ADR-0004`), and
the `warden plan` escalation verdicts (WP-0029).
Prefer **compilation over runtime lookup** for anything flex-auth evaluates: it
is latency-critical and already consumes a built registry snapshot. Adding a
synchronous zone lookup to the decision path would be a regression dressed as an
architecture.
```task ```task
id: WARDEN-WP-0032-T04 id: WARDEN-WP-0032-T04
status: todo status: todo
priority: medium priority: high
``` ```
**Make `policy.enabled` zone-aware and retire the global boolean.** The **Declaration format and conformance rule.** Follow `tenancy.yaml`: a per-repo
successor control reads the zone of the actor being signed for. Ship the declaration, with evidence, a `reviewed` date and a `review_due`, and the §6
migration in the same change as the deprecation — leaving both is exactly the rule carried over — **accuracy, not altitude**. A repo declaring a stricter zone
second-source-of-truth failure ADR-0001 exists to prevent. than it can evidence is the failure mode to design against, because it is the
one that looks like progress.
Include how a zone assignment is *changed*, and how that change is observed. A
zone that can be quietly widened is not a boundary. If time-boxed exceptions
exist, their expiry must be enforced by something, not by an intention.
```task ```task
id: WARDEN-WP-0032-T05 id: WARDEN-WP-0032-T05
@ -117,7 +201,34 @@ status: todo
priority: medium priority: medium
``` ```
**Close WARDEN-WP-0031 T05 through this model** — enable the pre-sign gate for **Make `policy.enabled` zone-aware and retire the global boolean.** The
whichever zone the SSH signing lane lands in, with that zone's failure mode. successor control reads the zone of the actor being signed for and that zone's
Re-run `scripts/check_policy_caller_identity.py` first; the WP-0031 evidence failure mode. Ship deprecation and migration in the same change — leaving both
will be stale by then and re-establishing it is cheap by design. is the second-source-of-truth failure `ADR-0001` exists to prevent.
This closes `WARDEN-WP-0031-T05`, cancelled under `ADR-0006`. Re-run
`scripts/check_policy_caller_identity.py` first: the WP-0031 evidence
(`decision:f3f7c88f9585582a`, 2026-08-19) will be stale, and re-establishing it
is cheap precisely so this is a re-check rather than a re-do.
```task
id: WARDEN-WP-0032-T06
status: todo
priority: medium
```
**Close out honestly.** Update `SCOPE.md`, `wiki/WorkloadSecurityPosture.md`,
and `wiki/PolicyGatedSigning.md`. If the model landed in canon or in another
repo, `ADR-0006` gains a successor or an amendment recording where enforcement
scoping now lives — ops-warden follows it rather than owning it, and the ADR
must say which.
## Deliberately out of scope
- **Network segmentation.** "Zone" is an overloaded word; this model is about
*enforcement rigidity*, not routing or NetworkPolicy. Say so wherever the term
first appears, or the two will be conflated within a quarter.
- **Re-litigating the three existing axes.** They stay as they are unless T02
finds a fold-in.
- **Zones for anything ops-warden does not consume**, unless T01 hands the model
to an owner who needs the wider scope.