flex-auth reviewed ZONE-WP-0001 as the consuming PDP and two findings land on the ops-warden consumer side: T02 - fail_closed is not expressible by a PDP. Fail-open describes what warden sign does when flex-auth is unreachable, so no decision exists to carry it. The replacement for policy.enabled is two things: stance, which arrives in the decision, and failure mode, which stays local and is declared per zone. T02 - build_flex_auth_registry.py:77 already emits a hardcoded trust_zone: platform that no policy reads. Resolve that before compiling zone membership rather than adding security_zone beside it. T03 - the declaration must say whether a zone rides the actor (subject) or the lane (resource); in the compiled registry an actor is both. No flex-auth registry schema change is needed - metadata, labels and attributes already flatten into the rego input. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
147 lines
6.5 KiB
Markdown
147 lines
6.5 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.
|
|
|
|
```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 whatever format `ZONE-WP-0001-T05` settles,
|
|
alongside the existing posture descriptors. 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
|