ops-warden/docs/adr/ADR-0006-enforcement-is-zone-scoped.md
tegwick 8c58f8bfa1
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Hand the zone model to zone-engine; keep WP-0032 as the consumer side
zone-engine is seeded and owns the security zone model as ZONE-WP-0001. Under
ADR-0005 ops-warden implements one lane narrowly and routes the rest, and an
estate-wide enforcement model is not a lane to absorb — it was ops-warden's
deferred flip that exposed the gap, not ops-warden's model to define.

WARDEN-WP-0032 is rewritten as the consumer side: hand the estate inputs to
ZONE-WP-0001-T02 (27 catalog lanes, the actor inventory, the three posture axes,
the three controls the model must express, and the compiled-registry path),
then replace policy.enabled with a zone-aware control and amend ADR-0006 to say
ops-warden follows the model rather than owning it.

ADR-0006 and SCOPE updated to point at zone-engine, which joins the related
repositories table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:20:34 +02:00

103 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: ops-warden-adr-0006
type: adr
title: "ADR-0006 — Enforcement is zone-scoped, never a global flag"
domain: infotech
repo: ops-warden
status: accepted
version: "1.0"
revision: "1"
owner: ops-warden
binds: "ops-warden"
created: "2026-08-19"
updated: "2026-08-19"
last_reviewed: "2026-08-19"
review_interval: 6m
enforced_by: "warden.yaml policy.enabled; scripts/check_policy_caller_identity.py; zone-engine ZONE-WP-0001; WARDEN-WP-0032"
supersedes: ""
successor: ""
---
# ADR-0006 — Enforcement is zone-scoped, never a global flag
## Status
Accepted 2026-08-19, at the moment `policy.enabled: true` was ready to be set
and deliberately was not.
## Context
WARDEN-WP-0031 finished the calling side of the flex-auth pre-sign gate. The
flex-auth pin `flex-auth-ops-warden` runs `callerAuth.mode: enforce`; ops-warden
presents a bound ServiceAccount token; the readiness gate exits 0 against the
enforcing pin and an anonymous `/v1/check` is 401. Everything needed to set
`policy.enabled: true` was in place.
`policy.enabled` is a **single boolean over the whole repo**. Combined with
`fail_closed: true` it makes flex-auth a hard dependency of *every* `warden
sign` — including the certificates the ops-bridge tunnels depend on, one of
which is the tunnel carrying the policy call itself. A dead tunnel or a
recreated Service does not degrade signing; it stops it.
That cost might be acceptable for a settled production lane. It is not
acceptable uniformly, because ops-warden signs across an estate that is being
actively rebuilt. During deep refactors — CoulombCore's decommission, the
issue-core move, cluster rebuilds — the same flag would harden exactly the
access needed to *perform* the refactor. Security that stops the work stops
being security and starts being an outage with good intentions.
The repo already refuses to treat posture as one-dimensional. WP-0015 shipped
environment posture (`dev` / `test` / `prod`) and workload maturity (`M0``M3`);
WP-0029 added `organization_posture: build` as a third axis precisely because
the *organization's* state changes what is reasonable to demand. A global
`policy.enabled` contradicts all of that: it is a fourth control that ignores
the three axes already declared.
## Decision
**Enforcement posture is a property of a zone, not of the repo.** ops-warden
does not enable a fail-closed authorization gate globally. Before
`policy.enabled: true` is set anywhere, the zones must exist: named bands of
differing rigidity, each declaring what is enforced, what is advisory, and what
is exempt — and the gate must be scoped to them.
Concretely, until `zone-engine`'s `ZONE-WP-0001` defines the zone model:
- `policy.enabled` stays `false`. Its readiness is evidence, not a mandate.
- A gate that is *ready* is recorded as ready. Readiness is not a reason to
enable; deferral with a stated reason is a legitimate terminal state for a
task, not an unfinished one.
- Any future enforcement control ships zone-aware or does not ship. A second
global boolean is the defect this record exists to prevent.
## Consequences
**We accept** that the pre-sign gate remains unexercised in production longer,
and that the WP-0031 evidence ages. Re-running
`scripts/check_policy_caller_identity.py` re-establishes it cheaply, and the
readiness gate exists precisely so this is a re-check rather than a re-do.
**We accept** that flex-auth's `flex-auth-ops-warden` pin sits enforcing with no
enforcing consumer. That is not waste: it makes the anonymous path 401 rather
than a decision, which was the ADHOC-2026-08-17-T01 condition regardless of
whether ops-warden calls it.
**We reject** the framing that a ready control should be turned on because it is
ready. The question is not "does it work" but "which zone is this, and does this
zone want this failure mode."
**This binds future work.** A zone-blind enforcement flag proposed in any
ops-warden workplan is out of order under this ADR, and should be sent back to
the zone model rather than merged with a caveat in its description.
## Related
- `WARDEN-WP-0031` — the calling side that made the flip possible (T05 deferred
under this ADR)
- `zone-engine` `ZONE-WP-0001` — the zone model this record defers to, seeded
2026-08-19 as the owning repo
- `WARDEN-WP-0032` — ops-warden's consumer-side adoption
- `wiki/WorkloadSecurityPosture.md` — the two axes already shipped (WP-0015)
- `wiki/PolicyGatedSigning.md` — the gate itself
- `history/2026-08-19-flex-auth-caller-identity-evidence.md` — readiness evidence
- flex-auth `FLEX-WP-0016` — the enforcing pin