From 38a186d308c682728ba6b6b785c89f02ca908ad3 Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 19 Aug 2026 22:20:13 +0200 Subject: [PATCH] Refine SCOPE, add INTENT, fix the GOAL invariant flex-auth rejected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GOAL.md still carried the first-draft invariant — "nothing this repo builds sits synchronously in a decision path" — after flex-auth's review had rejected it as a latency guarantee wearing an authority guarantee's clothes. Under that wording zone-engine could compile enforced: false for a lane, flip warden sign from deny to allow with no flex-auth policy change, and be literally compliant. Replaced: identity and membership here, effect in a flex-auth policy package. Compiled-not- queried is demoted to a consequence of that, which is what it always was. SCOPE now records what the two reviews settled rather than what was proposed: separate standard (canon Decision 5.6), membership declared in tenancy.yaml's reserved zones: key, stance out of scope for controls flex-auth decides, the fail-open axis modelled PEP-side because a PDP structurally cannot express it, organization_posture an input rather than a declaration field, and reefs not ours. Plus the two inherited constraints: the dead trust_zone field already sitting where membership would go, and flex-auth's lack of a reload path. INTENT.md states the argument, including what would falsify the repo — the exception lifecycle not needing a runtime is called out as a legitimate outcome that should archive this repo rather than keep it for its own sake. Co-Authored-By: Claude Opus 5 --- GOAL.md | 18 +++++-- INTENT.md | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ SCOPE.md | 138 +++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 236 insertions(+), 47 deletions(-) create mode 100644 INTENT.md diff --git a/GOAL.md b/GOAL.md index 1f1906f..ff82bbb 100644 --- a/GOAL.md +++ b/GOAL.md @@ -17,9 +17,21 @@ expires on its own. ## Invariants -- **`flex-auth` remains the only policy decision point.** Zone membership - reaches it by compilation into the registry it already consumes. Nothing this - repo builds sits synchronously in a decision path. +- **`flex-auth` remains the only policy decision point.** zone-engine is + authority over zone **identity and membership**; the **effect** of a zone on + any decision flex-auth renders is expressed in a flex-auth policy package. + + The first draft of this invariant said "nothing this repo builds sits + synchronously in a decision path". flex-auth rejected that on review: it is a + *latency* guarantee, not an authority one. Compiled data that determines an + outcome is still deciding — it just decided earlier. Under the old wording + zone-engine could compile `enforced: false` for a lane, flip `warden sign` + from deny to allow with no flex-auth policy change, and be literally + compliant. Membership is ours; stance is theirs. +- **Compiled, not queried.** Membership reaches flex-auth by compilation into + the registry it already loads. This is a *consequence* of the invariant + above, not the invariant itself — flex-auth is `service_class: + latency-critical` and loads its registry once at process start. - **A zone that can be quietly widened is not a boundary.** Every change of stance is observable, and every exception has an enforced expiry. - **Accuracy, not altitude** (`tenancy-posture_v0.1` §6). A repo declaring a diff --git a/INTENT.md b/INTENT.md new file mode 100644 index 0000000..307d48c --- /dev/null +++ b/INTENT.md @@ -0,0 +1,127 @@ +# INTENT + +> Why `zone-engine` exists and where it is going. +> What is true today is `SCOPE.md`. Checkable gates and the retirement +> condition are `GOAL.md`. This file is the argument, not the checklist. + +--- + +## 1. The sentence this repo exists to make sayable + +> *"This control is enforced **there**, advisory **here**, and relaxed **in this +> band until Friday** — and all three of those are written down, reviewable, and +> expire on their own."* + +NetKingdom cannot say that today. It can say how exposed a workload is +(environment posture), how ready it is (`M0`–`M3`), and what state the +organization is in (`organization_posture`). Every one of those **describes**. +None of them **decides**. So every enforcement control in the estate has been a +boolean over a whole repo. + +## 2. Where that bit + +ops-warden built a pre-sign authorization gate, flex-auth enforced its side, and +the gate verified clean. Then it was deliberately not switched on. + +`policy.enabled` is one boolean over the entire repo. With `fail_closed: true` +it makes flex-auth a hard dependency of *every* `warden sign` — including the +SSH certificates the ops-bridge tunnels depend on, one of which carries the +policy call itself. Turning it on would have been correct for a settled +production lane and wrong for an estate in the middle of decommissioning one +host and moving a service off it. + +That is not a story about a flag. **Security that stops the work stops being +security and becomes an outage with good intentions** — and the reason it would +have was structural: there was nowhere to say *where* it applies. The decision +is ops-warden's `ADR-0006`; this repo is what it defers to. + +## 3. What a zone has to be, to be worth having + +A classification that changes nothing is a label. A zone earns its keep only if +it decides something, so the model must carry: + +- **Membership** — what puts a lane, actor or workload in a zone, derived from + posture already declared rather than a fourth hand-maintained list. +- **Stance per control** — enforced, advisory, or exempt. +- **Failure mode** — what happens when the control cannot run. This is where + the bite is; `fail_closed` is what turns a dead tunnel into an outage. +- **Exceptions that expire without anyone remembering them.** + +The last is the reason this is a repo rather than a document. A standing +classification needs no engine — canon plus a declaration file covers it, which +is exactly how tenancy posture works. **A relaxation with an expiry is state**, +and state wants an owner, an audit trail, and something other than good +intentions enforcing the deadline. + +## 4. The line this repo must not cross + +`flex-auth` is the policy decision point. It stays the only one. + +zone-engine is authority over zone **identity and membership**. The **effect** +of a zone on a decision flex-auth renders belongs in a flex-auth policy package, +because flex-auth's decision envelope stamps `matched_policy_version` and +`policy_package` — and registry content appears nowhere in that provenance. Put +stance in the registry and two decisions with the same policy version and the +same request can differ, with nothing in the audit trail explaining why. + +That boundary was drawn by flex-auth on review, against a first draft of ours +that got it wrong in an instructive way: the original invariant forbade sitting +*synchronously* in a decision path, which is a latency guarantee wearing an +authority guarantee's clothes. Compiled data that determines an outcome is still +deciding; it just decided earlier. + +**Held to, in one line: membership is ours, stance is theirs, and the decision +point is neither.** + +## 5. What success looks like from outside + +Someone who has never read this repo should be able to: + +- open a service's `tenancy.yaml`, read its `zones:` key, and know which + controls bite it; +- ask "why was this relaxed?" and get a justification, an author and an expiry, + not a shrug; +- watch a relaxation lapse **without a human remembering it**; +- and find the rule published in NetKingdom canon rather than in this repo's + prose. + +If a reader has to come here to understand their own posture, the model has +failed at being a standard and has succeeded only at being a service. + +## 6. What would make this repo wrong + +Stated up front, because a repo created on a recommendation should say what +would falsify it: + +- **The exception lifecycle turns out not to need a runtime.** If reviewed + declarations in git, with expiry evaluated at decision time, are sufficient, + then the honest outcome is a canon standard and no engine — and this repo + should be archived rather than kept for the sake of existing. `GOAL.md` gate 5 + and `ZONE-WP-0001-T04` both hold that door open deliberately. +- **The zones do not partition the real estate.** If the model needs a residue + of special cases to cover today's 27 routing lanes and actor inventory, it is + describing an aspiration, not a structure. +- **Nobody but the author declares one.** Two repos declaring and a third + reading is the minimum evidence of a model rather than a preference. +- **It becomes a second decision point.** The failure would not announce itself; + it would arrive as a small convenience — a stance compiled into membership + "just for now". + +## 7. What this repo deliberately will not become + +Not a PDP. Not network segmentation — "zone" is overloaded and the routing sense +is someone else's word. Not a placement model: reefs answer *where a thing runs* +and are `repo-manager`'s, and the fact that `reef-railiance` caps availability +for everything bound to it is a canon composition problem (`NK-WP-0027`), not +ours. Not an owner of anyone's controls — the controls stay with the repos that +enforce them; this model only says where they bite. + +## 8. Direction + +Model first, from the real estate. Canon standard drafted here and published by +`net-kingdom`, in the family of `tenancy-posture_v0.1`. Membership declared in +the `zones:` key that canon has already reserved. Two consumers before it is +called adopted, the first being ops-warden retiring `policy.enabled`. + +A runtime only if `ZONE-WP-0001-T04` proves the exception lifecycle needs one — +and the cheaper answer is a legitimate result, not a disappointment. diff --git a/SCOPE.md b/SCOPE.md index c9d153d..e13b7c7 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -1,15 +1,16 @@ # SCOPE > What this repository is about, when it is relevant, and when it is not. -> Direction lives in `GOAL.md`. +> The argument for its existence is `INTENT.md`; the checkable gates and the +> retirement condition are `GOAL.md`. --- ## One-liner Authority for security zones — the bands of enforcement rigidity that scope -*where* a control is enforced, and the lifecycle of time-boxed exceptions that -relax them during deep refactors without leaving a permanent hole. +*where* a control bites, and the lifecycle of time-boxed exceptions that relax +them during deep refactors without leaving a permanent hole. --- @@ -22,80 +23,129 @@ dependency of every `warden sign` — including the SSH certificates the ops-bridge tunnels depend on, one of which carries the policy call itself. Across an estate under continuous deep refactor, uniform enforcement hardens -exactly the access needed to perform the refactor. That is not security; it is -an outage with good intentions. The flip was deferred under ops-warden's -`ADR-0006` — *enforcement is zone-scoped, never a global flag* — and this repo -is what that ADR defers to. +exactly the access needed to perform the refactor. The flip was deferred under +ops-warden's `ADR-0006` — *enforcement is zone-scoped, never a global flag* — +and this repo is what that ADR defers to. --- ## In scope - **Zone identity and membership.** What zones exist, and what puts a lane, - actor, or workload in one. Derived from declared posture where possible - rather than a fourth hand-maintained list. -- **Enforcement stance per control per zone** — enforced / advisory / exempt, - and the failure mode (fail-closed or fail-open) that goes with it. The failure - mode is the part with teeth. -- **Time-boxed exception lifecycle** — a relaxation with an expiry that is - enforced by something rather than intended, plus the record of who widened - what, when, and until when. -- **The declaration format** repos use to state their zones, following - `tenancy.yaml` and its rule: *accuracy, not altitude*. -- **Drafting the canon standard** for the model, offered to `net-kingdom` for - publication the way `tenant-engine` and `user-engine` have boundary contracts - in canon. + actor or workload in one — derived from posture already declared rather than + a fourth hand-maintained list. +- **Time-boxed exception lifecycle.** A relaxation with an expiry enforced by + something rather than intended, plus the record of who widened what, when, + and until when. **This is the task that decides whether this repo needs a + runtime at all.** +- **The membership declaration**, carried in each repo's `tenancy.yaml` under + the reserved top-level `zones:` key, with `tenancy-posture_v0.1` §6 carried + over verbatim: *accuracy, not altitude*. +- **Drafting the canon standard**, offered to `net-kingdom` for publication in + the family of `tenancy-posture_v0.1` and the `*-engine` boundary contracts. +- **Naming stance vocabulary** — `enforced` / `advisory` / `exempt` — as + something owners express, not as something this repo evaluates. ## Out of scope - **Authorization decisions.** `flex-auth` is the PDP and stays the only one. - A second decision point is the split-brain this repo must not create. -- **Network segmentation.** "Zone" is overloaded; this is about enforcement - rigidity, not routing or NetworkPolicy. The two must not be conflated. -- **Substrate placement.** Reefs (`reef-railiance`, `reef-storage`) answer - *where does it run*; a zone answers *how rigidly is it governed*. Correlated, - not the same axis — and per repo-manager's rule, topology is not readiness. -- **Identity, secrets, tenancy.** `key-cape`, OpenBao / `secrets-engine`, +- **Stance for controls flex-auth decides.** Settled on review: for the + pre-sign gate, stance lives in a flex-auth **policy package**, not in + compiled membership. flex-auth's decision envelope stamps + `matched_policy_version` and `policy_package`, and registry content appears + nowhere in that provenance — stance in the registry would let two decisions + with the same policy version and the same request differ, with nothing in the + audit trail explaining why. **Membership is ours; stance is theirs.** +- **The fail-open / fail-closed axis.** Also settled on review: a PDP returns + an effect. Fail-open describes what a *policy enforcement point* does when + the PDP is unreachable — no decision is rendered, so no compiled data and no + policy rule can reach it. The failure-mode axis is modelled PEP-side, by the + repo that owns the control. +- **`organization_posture`.** Ruled out of the declaration by canon: a + fleet-wide, time-varying scalar describing the estate is not a property of a + declaring service, and a per-repo copy of a global goes stale in as many + places as there are repos. It is an **input** to stance selection, read by + the model, never absorbed into it. +- **Network segmentation.** "Zone" is overloaded; the routing sense is someone + else's word. +- **Substrate placement.** Reefs are `repo-manager`'s. That + `reef-railiance` is single-node and therefore *caps availability* for + everything bound to it is a canon composition defect (`NK-WP-0027`), not a + zone problem — and zone-engine is explicitly not blocked on it. +- **Identity, secrets, tenancy** — `key-cape`, OpenBao / `secrets-engine`, `tenant-engine`. -- **Implementing any specific control.** Owners keep their own controls; this - repo says where they bite. +- **Implementing anyone's controls.** Owners keep them; this model says where + they bite. - **Publishing canon.** `net-kingdom` owns canon; `policy-nexus` publishes. --- +## Settled by review (2026-08-19) + +`ZONE-WP-0001` was reviewed by both affected owners before any modelling began. +Their answers are binding on this repo and are recorded in the workplan. + +| Question | Answer | Ruled by | +| --- | --- | --- | +| A new standard, or a seventh axis of `tenancy-posture_v0.1`? | **Separate standard.** The six axes are ladders where higher is stronger and desirable; enforcement stance is not monotone — `ADR-0006` *is* the finding that the top rung is wrong for the SSH lane. And an accurately declared `exempt` as an axis would be conformant *and* exempt: a conformance rule handing out its own exemption. | net-kingdom, Decision 5.6 | +| Then where is membership declared? | **`tenancy.yaml`, reserved top-level `zones:` key.** One declaration surface, one review cadence, one validator; two standards, because they have different owners and different conformance semantics. | net-kingdom, Decision 5.6 | +| Does membership need a flex-auth schema change? | **No.** Resource `metadata` / `labels` / `attributes` are already flattened into the Rego input, so a compiler emitting a zone field is readable today. | flex-auth | +| Is "never synchronously in a decision path" the right invariant? | **No** — that is a latency guarantee, not an authority one. Replaced: identity and membership here, effect in a flex-auth policy package. | flex-auth | +| Are reefs ours to reconcile? | **No**, and the attempt surfaced a canon defect instead (`NK-WP-0027`). | net-kingdom | + +### Two constraints inherited from that review + +- **`trust_zone` already exists inside the PDP, and is dead.** + `ops-warden/scripts/build_flex_auth_registry.py` hardcodes + `"trust_zone": "platform"` on every ssh-cert resource; it reaches the Rego + input and **no policy rule reads it**. This repo's own warning about "zone" + being overloaded named network segmentation as the hazard; the live collision + is a dormant, plausibly-named field sitting exactly where membership would go. + The compiler must resolve it — reuse or rename, deliberately. +- **flex-auth has no reload path.** Registry and policy are loaded once at + process start from a digest-pinned image. An exception compiled as inert + registry data therefore expires only when a human redeploys — expiry by + *intention*, which is precisely what the exception lifecycle must not be. An + exception carrying `not_after`, evaluated against decision time in policy, + does expire on its own: granting costs a redeploy, lapsing is automatic. + +--- + ## Relationship to what already exists | Mechanism | Owner | Relationship | | --- | --- | --- | -| `tenancy-posture_v0.1` (six axes, per-repo `tenancy.yaml`) | net-kingdom canon | **Structural model to follow** — graduated levels, declared conformance, review dates, "accuracy not altitude". Its §14 stance is *structure, not tooling*, which is why this repo starts as a model and not an API | -| Environment posture + workload maturity `M0`–`M3` | ops-warden (WP-0015) | Inputs a zone may derive membership from | -| `organization_posture: build` | ops-warden (WP-0029) | Candidate to fold in rather than run alongside | -| Reefs / `bound_reefs` | repo-manager | Correlated substrate axis; must be reconciled, not duplicated | -| Compiled registry snapshot | flex-auth | **How zone membership reaches the PDP.** flex-auth is latency-critical; compilation, not runtime lookup | +| `tenancy-posture_v0.1` | net-kingdom canon | Sibling standard and **carrier file**. Structural model: graduated levels, per-repo declaration with evidence and review dates, accuracy over altitude | +| Environment posture + workload maturity `M0`–`M3` | ops-warden (WP-0015) | Candidate membership inputs | +| `organization_posture: build` | ops-warden (WP-0029) | An **input** to stance selection. Explicitly not part of the declaration | +| Reefs / `bound_reefs` | repo-manager | Separate axis. Their interaction with availability is `NK-WP-0027`, not ours | +| Compiled registry snapshot | flex-auth | How membership reaches the PDP — no schema change needed, one name collision to resolve | +| Policy package | flex-auth | Where **stance** lives for controls flex-auth decides | | `warden plan` verdicts + `reasons` | ops-warden (WP-0029) | Existing verdict machinery to extend, not parallel | --- ## Current state (2026-08-19) -Seeded. No model, no schema, no runtime. `ZONE-WP-0001` derives the model from -the real estate and drafts the canon standard; a runtime is earned only if the -exception lifecycle proves to need one. +Seeded and reviewed. **No model, no schema, no runtime** — deliberately: an +engine that ships a wire format before it has partitioned the real estate +defines the model by accident. -Deliberately empty of schema — an empty engine that ships a schema first ends up -defining the model by accident. +`ZONE-WP-0001` carries the work. T01 is answered by both owners; T02–T07 are +open. Whether a runtime is warranted is an *output* of T04, and the "no runtime" +answer is a legitimate result that would retire this repo rather than embarrass +it (`GOAL.md` gate 5). --- ## Relevant when -- A control is about to be enabled and the question "enforced *where*?" has no - answer +- A control is about to be enabled and "enforced *where*?" has no answer - A deep refactor needs relaxed rigidity in a band of the estate, with an expiry -- A repo needs to declare which zone its lanes sit in +- A repo is filling in the `zones:` key of its `tenancy.yaml` ## Not relevant when -- Asking whether a specific request is allowed (→ `flex-auth`) -- Placing a workload on a substrate (→ `repo-manager` reefs) +- Asking whether a specific request is allowed → `flex-auth` +- Placing a workload on a substrate → `repo-manager` reefs - Anything about network reachability