zone-engine/docs/estate-partition-2026-08-19.md
tegwick de2d8e2dda Correct the rapp count: eight declarations, not nine
An inflated count in a document whose subject is accuracy.

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

232 lines
12 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.

# Estate partition — ZONE-WP-0001-T02
**Date:** 2026-08-19 · **Corpus:** ops-warden `registry/routing/catalog.yaml`
(27 entries), `inventory.yaml` (4 actors), `registry/policy/security-posture.yaml`
(3 env postures, 4 maturity levels).
The test set by T02: *the model must partition what exists without a residue of
unexplained exceptions, and the exceptions found are the most informative
output.* This is the raw result, before any model is fitted to it.
---
## 1. What the data actually says
Cross-tabulating the three declared fields that could plausibly drive
membership — `delegation.mode`, `risk`, `exec_capable` — fills **9 of 27
possible cells**. The estate clusters hard.
| delegation | risk | exec_capable | n | character |
| --- | --- | --- | --- | --- |
| `permanent` | ungraded | — | 1 | The one lane ops-warden decides (`ssh-cert-host-access`) |
| `native` | ungraded | — | 7 | Pure pointers: route to an owner, nothing executes |
| `native` | ungraded | `false`/`true` | 2 | Owner-fronted exec (`whynot-design-npm-publish`, `audit-core-senders`) |
| `native` | high | `false` | 1 | `database-dynamic-credentials` — owner-fronted, dangerous |
| `interim` | ungraded | `true` | 4 | **ops-warden proxying, ungraded** |
| `interim` | standard | `false` | 2 | Interim, graded, not proxyable |
| `interim` | high | `false` | 2 | Interim, dangerous, routed only |
| `interim` | high | `true` | 8 | **ops-warden proxying a high-risk credential for an absent owner** |
The three fields are **not independent**, and that is the finding. Danger
concentrates in `interim`: 10 of the 11 high-risk lanes are interim, and 8 of
those are `exec_capable`. `permanent` and `native` are almost entirely benign.
**Delegation mode is doing most of the discriminating work.** It was introduced
by WP-0030 to answer a governance question — *should ops-warden own this?* — and
it turns out to predict operational danger better than the field named `risk`.
## 2. The bands that exist
Four, derived rather than invented. Every one of the 27 lanes lands in exactly
one.
| Band | n | Defining property | What it means for a control |
| --- | --- | --- | --- |
| **Decided** | 1 | `warden_executes: true` — ops-warden is the decision point | The only place a pre-sign gate can bite at all |
| **Fronted** | 10 | `delegation.mode` is `native` — the owner has a front door | Stance is the *owner's*; ops-warden holds nothing |
| **Covered** | 12 | `interim` and `exec_capable: true` — ops-warden proxies a value it does not own | The band that actually needs rigidity, and the band a refactor most needs relaxed |
| **Signposted** | 4 | `interim`, not `exec_capable` — routed, nothing flows | Advisory at most; there is no value to leak |
The interesting boundary is **Covered vs Fronted**, and it is not a risk
judgement — it is a statement about *whether the owning component exists yet*.
That is why it moves over time and why bands must be re-derived, not assigned
once.
## 3. The existing controls already imply bands — and they disagree
Before any zone model, ops-warden's three controls each cut the estate
differently:
| Control | Lanes touched | Selector |
| --- | --- | --- |
| flex-auth pre-sign gate | **1** | `warden_executes` |
| fetch policy gate | **13** | `exec_capable` |
| agent read-boundary (`ADR-0004`) | **11** | `risk == "high"` |
| `warden plan` escalation | 27 | all |
Only **8 lanes** are in both the fetch gate and the read-boundary. The controls
were built independently and their scopes were never reconciled. A zone model
that assigns one zone per lane and expects all controls to agree with it will
misfit immediately — which answers a question T03 was going to have to ask:
**stance is per control per zone, never per zone alone.**
## 4. The residue — and it is the most valuable output
**14 of 27 lanes carry no `risk` value at all.**
This is not a modelling inconvenience. `is_high_risk` is `risk == "high"`
(`routing/models.py:123`), so an ungraded lane never triggers the agent
read-boundary. Five of the fourteen are `exec_capable: true`, meaning
`warden access --fetch` can stream a real value for them to an agent session:
`openbao-api-key`, `whynot-design-npm-publish`, `key-cape-oidc-login`,
`issue-core-ingestion-api-key`, `reuse-surface-hub-write-token`.
`ADR-0004` reads as a categorical rule. The implementation is an opt-in list,
and absence fails open.
**Routed to `risk-nexus` as `RISK-F-0003`.** It is a defect in a shipped
control, not a known gap being worked — the distinction ops-warden's own
finding-routing rule draws. Severity is risk-nexus's to set.
For this task it also means: **a zone model cannot be derived from `risk` as it
stands.** Half the corpus is unlabelled on that field, and grading it is real
judgement, not backfill.
Two further gaps, less severe but structural:
- `warden_role` and `has_rotation` are **derived at load, not declared** — they
are absent from every raw entry despite appearing in `warden route --json`. A
membership compiler must read the model, not the file.
- The posture registry (`env_postures`, `M0``M3`) and the catalog share **no
join key**. No lane references a workload or an environment. They are
currently disjoint vocabularies.
## 5. Answers to T02's questions
### Is a zone a property of the actor, the lane, the workload, or the moment?
> **Corrected 2026-08-19 by operator direction, after this analysis was
> written.** The answer below ("the lane") mistook the corpus for the subject.
> **Security policy is always about the actual workload.** ops-warden's catalog
> is a credential surface, not a workload inventory, so lane properties were the
> only thing available to partition — and partitioning what is available is not
> the same as finding the policy subject. The bands in §2 stand as an accurate
> description of ops-warden's credential surface and remain useful membership
> *inputs*; they are not zones. See §7.
**The lane**, on the evidence available in this corpus. Every field that discriminates —
`delegation.mode`, `exec_capable`, `risk` — is a lane property. The actor
inventory holds 4 actors mapping to a single lane and cannot carry a partition.
No lane references a workload.
With two qualifications the data forces:
- **The caller is a modifier, not the zone.** The read-boundary keys on
`WARDEN_AGENT_ID` — a property of *who is asking*, orthogonal to the lane. A
zone says what the lane requires; the caller decides which requirement
applies. Collapsing them would make one lane need two zones.
- **The moment is a different object, confirmed.** Nothing in the corpus
represents it. `delegation.blocked_on` carries a free-text claim with a
`reviewed` date and no expiry — precisely the "expiry by intention" shape
`ZONE-WP-0001-T04` must not reproduce. A time-boxed relaxation is an
**exception against a zone**, never a zone.
### Do zones compose with, or fold in, environment posture and `M0``M3`?
**Compose — and the composition cannot be built yet.** They are per-workload;
zones as derived here are per-lane; and there is **no join key between them in
any registry today**. Folding them in would require inventing a lane→workload
mapping that does not exist, which is fabrication under §6.
The honest sequence: zones stand alone on lane properties now, and a join is
proposed only when something actually declares it. (`organization_posture` was
already ruled out of the declaration entirely by net-kingdom — it is an input.)
### Reefs
Struck by net-kingdom. Per `tenancy-posture` Decision 8.4.1, substrate location
is never by itself evidence for a level. The unreconciled pair is reef ↔ `P`/`V`
and it is canon's, tracked as `NK-WP-0027`.
## 6. What this hands to T03 and T04
- Four bands, each lane in exactly one, derived from declared fields.
- **Stance must be per control per zone** — the existing controls demonstrably
disagree about scope.
- **`Covered` is the band the model exists for**: 12 lanes where ops-warden
proxies what it does not own, holding the highest concentration of high-risk
`exec_capable` lanes, and the band a deep refactor most needs relaxed.
- **`risk` is not usable as a membership input until the 14 ungraded lanes are
graded** (`RISK-F-0003`). `delegation.mode` is usable today and is a better
predictor.
- Membership must be computed from the loaded model, not read from raw YAML.
---
## 7. Correction — the subject is the workload, and a zone is an admission standard
Operator direction, 2026-08-19, after §1§6 were written:
> *Security policy is always about the actual workload. The repository providing
> the software may suggest security policy for running workloads in different
> situations, but the workload — and whoever is responsible for it — defines the
> level of scrutiny. Some zones may well define standards that have to be met in
> order to run as a workload in that zone.*
This reverses the direction the analysis above assumed. It had zones as a
**classification derived from** properties. They are **bands with admission
standards**: a workload does not get labelled with a zone, it *qualifies* to run
in one.
### Three roles, not one
| Role | Who | Authority |
| --- | --- | --- |
| **Suggests** | The repository providing the software | Recommends a posture for running its software in given situations. A recommendation, never a determination — it does not know where its software will run |
| **Declares** | The workload and its responsible party | Sets the level of scrutiny actually applied. **This is the policy subject.** |
| **Requires** | The zone | Defines the standard a workload must meet to be admitted. A floor, not a label |
### This is canon's existing mechanism, not a new one
`tenancy-posture_v0.1` **Decision 8.2 — split authority, machine-reconciled**
already partitions authority this way: the package repo owns substrate numbers
and enforcement, *"the consuming repo owns its workload requirements"*, and a
tier minimum is owned separately and **joined by machine** at definition and
whenever either side changes — because *"a machine-checkable constraint must not
depend on somebody remembering to collect a signature."*
And Decision 5.6, ruling on this very standard, already said stance *"behaves
like a tier minimum under Decision 8.2 — asserted elsewhere, joined by machine."*
So the zone model does not need a new prescription mechanism. It needs to **be**
a tier minimum: the zone asserts a floor, the workload asserts its posture, and
the join is mechanical. That also resolves the grade-versus-acceptance split
raised for T03 — they are simply the two sides of the join.
### What this does to the bands in §2
They survive as *inputs*, demoted from conclusions. `Decided` / `Fronted` /
`Covered` / `Signposted` accurately describe ops-warden's credential surface and
still predict where danger concentrates. What they cannot do is name the entity
policy applies to, because ops-warden's catalog contains no workloads.
The missing join found in §4 is therefore not a tidy-up. **It is the model.**
> **Corrected 2026-08-20.** "No registry carries a join key" was too strong — it
> was concluded from ops-warden's catalog alone, the one place a *workload*
> declaration would not live. `rapp-*/declarations/rapp.yaml` declares
> `workload_identity` with `data_classification` and `criticality` for eight
> workloads, and ops-warden's `dataclass_floor` already maps classification to
> `M0``M3`. The chain exists and spans two repos. What is genuinely missing is
> an explicit `workload:` field on catalog entries — the key is currently only
> derivable by parsing `path_template`, whose convention is inconsistent. See
> `ZONE-WP-0001-T03`.
### Build stage
The organization is in `build`. The default stance is therefore permissive, and
that is a positional fact rather than a concession: `ADR-0006` deferred a global
gate for exactly this reason. Zones tighten by **admission to a zone**, not by
raising a global floor — which is what makes the model compatible with deep
refactoring instead of hostile to it.