Amended by net-kingdom as canon owner of tenancy-posture_v0.1, answering ZONE-WP-0001-T01. T01: organization_posture does not fold into zones and does not belong in a per-repo declaration — it is a fleet-wide time-varying scalar. Hand it over as an input. T03: zone membership is declared in tenancy.yaml under a reserved zones: key (canon Decision 5.6). ops-warden has no tenancy.yaml, so this now implies declaring the six-axis posture vector too. Also records why this declaration may not set stance: a declarer that sets its own stance makes exempt conformant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
183 lines
8.8 KiB
Markdown
183 lines
8.8 KiB
Markdown
---
|
||
id: WARDEN-WP-0032
|
||
type: workplan
|
||
title: "Adopt security zones as a consumer — retire the global policy.enabled"
|
||
domain: infotech
|
||
repo: ops-warden
|
||
status: proposed
|
||
owner: ops-warden
|
||
topic_slug: netkingdom
|
||
planning_priority: P1
|
||
depends_on_workplans:
|
||
- WARDEN-WP-0031
|
||
created: "2026-08-19"
|
||
updated: "2026-08-19"
|
||
state_hub_workstream_id: "f1b6cdcb-5e16-48ab-a72b-339e909e92f0"
|
||
---
|
||
|
||
# WARDEN-WP-0032 — Adopt security zones as a consumer
|
||
|
||
`ADR-0006` defers `policy.enabled: true` until enforcement can be scoped to a
|
||
zone. **The zone model itself is no longer ops-warden's work.** It moved to
|
||
`zone-engine` as `ZONE-WP-0001` on 2026-08-19, where it belongs under `ADR-0005`
|
||
— ops-warden implements one lane narrowly and routes the rest, and an
|
||
estate-wide enforcement model is not a lane it should absorb.
|
||
|
||
What stays here is the consumer side: ops-warden was the repo whose deferred
|
||
flip exposed the gap, it holds the controls the model must be able to express,
|
||
and it is the first consumer the model has to satisfy.
|
||
|
||
## What ops-warden owes zone-engine
|
||
|
||
Inputs, not designs. `ZONE-WP-0001-T02` derives the model from the real estate,
|
||
and most of that estate is ops-warden's:
|
||
|
||
- **27 routing catalog lanes** (`registry/routing/catalog.yaml`) with `risk`,
|
||
`status`, and `delegation` already on each — the likeliest membership inputs.
|
||
- **The actor inventory** (`adm` / `agt` / `atm`) and its TTL policy.
|
||
- **The three posture axes already shipped** — environment and maturity
|
||
(WP-0015), `organization_posture` (WP-0029) — including the honest answer to
|
||
whether `organization_posture` should be folded into zones rather than run
|
||
beside them.
|
||
- **Three controls the model must be able to express**: 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.
|
||
- **The compiled-registry path** (`scripts/build_flex_auth_registry.py`) — how
|
||
membership can reach flex-auth without a lookup in a latency-critical decision
|
||
path.
|
||
|
||
## Tasks
|
||
|
||
```task
|
||
id: WARDEN-WP-0032-T01
|
||
status: todo
|
||
priority: high
|
||
state_hub_task_id: "b6dac514-4b05-47b8-8745-6a1c67b6100e"
|
||
```
|
||
|
||
**Hand the estate inputs to `ZONE-WP-0001-T02`.** Not a design proposal — the
|
||
lanes, actors, axes, and controls above, with the exceptions ops-warden already
|
||
knows do not fit cleanly (the interim delegation lanes from WP-0030 are the
|
||
obvious candidates: a lane covered on someone else's behalf may not sit in the
|
||
same zone as one ops-warden owns outright).
|
||
|
||
Include the `organization_posture` fold-in question honestly, including the case
|
||
against keeping it.
|
||
|
||
**Answered by net-kingdom 2026-08-19** (canon owner of `tenancy-posture_v0.1`,
|
||
answering `ZONE-WP-0001-T01`): **do not fold it in, and do not put it in a
|
||
per-repo declaration under any key.** `organization_posture` is a fleet-wide,
|
||
time-varying scalar describing the *estate*, not a property of a declaring
|
||
service; a per-repo copy of a global goes stale in as many places as there are
|
||
repos. It stays what WP-0029 made it — an input to stance selection that the
|
||
zone model reads. Hand it over as an input, not as a candidate axis. Environment
|
||
posture and `M0`–`M3` are per-workload and remain genuinely composable; those two
|
||
are still open for `ZONE-WP-0001-T02`.
|
||
|
||
```task
|
||
id: WARDEN-WP-0032-T02
|
||
status: wait
|
||
priority: high
|
||
state_hub_task_id: "08735f17-fe7e-4a6a-9b71-f350f98c30b1"
|
||
```
|
||
|
||
**Replace `policy.enabled` with a zone-aware control.** Waits on
|
||
`ZONE-WP-0001-T03` and `T05`. Reads the zone of the actor being signed for and
|
||
that zone's failure mode. Ship deprecation and migration in the same change —
|
||
leaving both is the second-source-of-truth failure `ADR-0001` exists to prevent.
|
||
|
||
Re-run `scripts/check_policy_caller_identity.py` before enabling anything: 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.
|
||
|
||
This closes `WARDEN-WP-0031-T05`.
|
||
|
||
**Amended by flex-auth 2026-08-19, reviewing `ZONE-WP-0001` as the PDP.** Two
|
||
constraints that change what this task can build, both argued in full at
|
||
`ZONE-WP-0001-T03` and `T04`:
|
||
|
||
1. **The zone-aware control splits across the boundary, it does not move.**
|
||
flex-auth will read compiled zone *membership* from the registry and apply the
|
||
per-zone *stance* from its policy package, returning an effect plus an
|
||
advisory annotation. What stays on the ops-warden side is the axis flex-auth
|
||
structurally cannot carry: **`fail_closed` is not expressible by a PDP.**
|
||
Fail-open describes what `warden sign` does when flex-auth is *unreachable* —
|
||
no decision is rendered, so no compiled data and no policy rule can reach it.
|
||
So the replacement for `policy.enabled` is not one zone-derived boolean
|
||
either; it is (a) stance, which arrives *in the decision*, and (b) failure
|
||
mode, which stays a local ops-warden setting declared per zone. Plan for two,
|
||
or T02 will be built expecting flex-auth to answer something it cannot.
|
||
|
||
2. **The membership compiler is ops-warden's to change, and it has a name
|
||
collision to resolve first.** `scripts/build_flex_auth_registry.py:77` already
|
||
emits `"trust_zone": "platform"` as a hardcoded constant on every
|
||
ssh-certificate resource. It is a first-class field on flex-auth's `Resource`,
|
||
it is surfaced into rego input, and **no policy package reads it**. Before
|
||
compiling zone membership, either retire that constant or deliberately
|
||
repurpose it — do not add `security_zone` beside a dormant `trust_zone` and
|
||
leave the next reader to guess which is real. That is the `ADR-0001`
|
||
second-source-of-truth failure in miniature, inside a generated artifact.
|
||
|
||
Good news for T03's cost: **no flex-auth registry schema change is required.**
|
||
`metadata`, `labels` and `attributes` already flatten into the rego input, so the
|
||
compiler can emit membership today.
|
||
|
||
```task
|
||
id: WARDEN-WP-0032-T03
|
||
status: wait
|
||
priority: medium
|
||
state_hub_task_id: "fd642377-8d64-473c-b684-f8f1a842c223"
|
||
```
|
||
|
||
**Declare ops-warden's zones** in the format `ZONE-WP-0001-T05` settles,
|
||
alongside the existing posture descriptors.
|
||
|
||
**Amended by net-kingdom 2026-08-19 — the carrier file is already settled, only
|
||
its contents are open.** Canon Decision 5.6 (`tenancy-posture_v0.1` draft-9)
|
||
rules that zone membership is declared in **`tenancy.yaml` under a reserved
|
||
top-level `zones:` key**, sibling to `tenancy:` and `provider:` and never inside
|
||
`tenancy.current`. §5.4 makes that file the repo's single posture declaration
|
||
surface, and a second root file would be the second-source-of-truth failure
|
||
`ADR-0001` exists to prevent — in ops-warden's own idiom. The key is reserved and
|
||
deliberately unconstrained in `net-kingdom/canon/schemas/tenancy-posture_v0.1.schema.json`,
|
||
so a combined declaration validates today.
|
||
|
||
Note the consequence for this repo: **ops-warden does not currently have a
|
||
`tenancy.yaml`.** Declaring zones means writing one, which means declaring the
|
||
six-axis posture vector too. That is a real and probably overdue cost, not an
|
||
accident of this amendment — canon would rather ops-warden declare both
|
||
accurately than declare a zone with no posture beside it.
|
||
|
||
Note also *why* stance is not something this declaration may set: canon Decision
|
||
5.6 rules that a declarer must not be the party that sets the stance, or an
|
||
accurately declared `exempt` becomes conformant *and* exempt. ops-warden declares
|
||
which zone a lane or actor is in. The stance of the pre-sign gate in that zone is
|
||
flex-auth's policy package; the failure mode is ops-warden's own setting, per
|
||
flex-auth's T03 amendment. Per flex-auth's T05 amendment, this
|
||
declaration must also say whether a zone rides the **actor** (a flex-auth
|
||
subject) or the **lane** (a flex-auth per-actor resource) — in the compiled
|
||
registry an actor is both, and the compiler needs to be told which record
|
||
carries membership. Carry the conformance rule:
|
||
*accuracy, not altitude*. Declaring a stricter zone than can be evidenced is the
|
||
failure mode that looks like progress.
|
||
|
||
```task
|
||
id: WARDEN-WP-0032-T04
|
||
status: wait
|
||
priority: medium
|
||
state_hub_task_id: "1af3d26e-ce21-46ca-a56c-486d52b76dcb"
|
||
```
|
||
|
||
**Amend `ADR-0006`.** Once zones exist and enforcement scoping is owned by
|
||
`zone-engine`, ADR-0006 must say that ops-warden *follows* the model rather than
|
||
owning it — a superseding record, never an in-place edit. Update `SCOPE.md`,
|
||
`wiki/WorkloadSecurityPosture.md`, and `wiki/PolicyGatedSigning.md` with it.
|
||
|
||
## Related
|
||
|
||
- `zone-engine` `ZONE-WP-0001` — the model, and where this work is led from
|
||
- `ADR-0006` — enforcement is zone-scoped, never a global flag
|
||
- `WARDEN-WP-0031` — the deferred flip and its readiness evidence
|
||
- `net-kingdom` `tenancy-posture_v0.1` draft-9 Decisions 5.6, 8.4.1, 8.4.2 — canon's
|
||
answer to `ZONE-WP-0001-T01`; enforcement stance is a sibling standard, not a
|
||
seventh axis, and the declaration surface is `tenancy.yaml`
|