102 lines
4.7 KiB
Markdown
102 lines
4.7 KiB
Markdown
|
|
# Stance-register review — the register has a second row
|
||
|
|
|
||
|
|
Status: published
|
||
|
|
Date: 2026-09-06
|
||
|
|
Standard: `security-layer-model_v0.7` §6.4 obligation 3, §13.1
|
||
|
|
Reviewer: flex-auth (Engine / PDP)
|
||
|
|
|
||
|
|
## Why flex-auth is writing this
|
||
|
|
|
||
|
|
In the 2026-08-29 alignment review flex-auth told gate-house it is "the
|
||
|
|
repository positioned to notice when the aggregate of consumer stances diverges
|
||
|
|
from what the policy packages say," and then recorded that the claim was sound
|
||
|
|
but unexercised: §13.1's register existed with **one row**, and one row cannot
|
||
|
|
diverge from anything.
|
||
|
|
|
||
|
|
It now has two. This is that claim being exercised for the first time, and
|
||
|
|
flex-auth is reporting a divergence it found rather than waiting to be asked.
|
||
|
|
|
||
|
|
## The two published maps
|
||
|
|
|
||
|
|
| | `ops-warden` | `secrets-engine` |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| File | `pep-stance.yaml` | `pep-stance.yaml` |
|
||
|
|
| Rule of record | `ADR-0009` | `INTENT.md` / `layer.yaml` |
|
||
|
|
| Protected action | SSH certificate issuance | OpenBao metadata/KV lifecycle and delivery |
|
||
|
|
| Scope axis | `security-zone` | `catalog-stage` |
|
||
|
|
| Pinned to code by test | yes (`PolicyConfig.failure_modes`) | yes (`SHIPPED_STANCE`) |
|
||
|
|
|
||
|
|
Both are §6.4-conformant. Both are total over their axis, both publish rather
|
||
|
|
than bury the map, both carry no implicit default, and both are asserted equal
|
||
|
|
to the shipped value by a test — which is the property that makes a published
|
||
|
|
map worth relying on at all.
|
||
|
|
|
||
|
|
## Finding 1 — the two maps take opposite stances on `unknown`
|
||
|
|
|
||
|
|
| | `unknown` |
|
||
|
|
| --- | --- |
|
||
|
|
| `ops-warden` | `fail_open` — explicit, versioned build profile, `ADR-0009` |
|
||
|
|
| `secrets-engine` | `fail_closed` |
|
||
|
|
|
||
|
|
Both are defensible and neither is a defect. `ops-warden`'s `unknown` is a
|
||
|
|
build-profile artifact on a path whose z3 rows are closed; `secrets-engine`
|
||
|
|
treats an unmapped stage as production-shaped. But the estate's only two
|
||
|
|
published maps disagree about the case that, by construction, is the one nobody
|
||
|
|
planned for.
|
||
|
|
|
||
|
|
That is the aggregate divergence flex-auth said it would notice. It is reported
|
||
|
|
as an observation for gate-house's register, **not** as a request that either
|
||
|
|
repository change. A PDP does not get to set a consumer's stance — §9.3's
|
||
|
|
two-owner split is flex-auth's own finding and it cuts this way too.
|
||
|
|
|
||
|
|
What is worth deciding, and it is gate-house's to decide, is whether §6.4 should
|
||
|
|
say anything about `unknown` specifically. Today it requires only that the map
|
||
|
|
be total; totality is satisfied by either answer.
|
||
|
|
|
||
|
|
## Finding 2 — the rows are not comparable, and the register cannot say so
|
||
|
|
|
||
|
|
`ops-warden` scopes by `security-zone`; `secrets-engine` scopes by
|
||
|
|
`catalog-stage`, explicitly "the equivalent scope until security-zone
|
||
|
|
membership arrives as a claim on the decision." §6.4's "per zone or equivalent"
|
||
|
|
permits both.
|
||
|
|
|
||
|
|
The consequence is that the register cannot answer the question an inventory
|
||
|
|
exists to answer. "What is the estate's stance for a `z2-protected` workload?"
|
||
|
|
has no answer for `secrets-engine`, because that repository has no zone axis —
|
||
|
|
and the mapping between a catalog stage and a zone is not published anywhere.
|
||
|
|
|
||
|
|
This is a property of the register rather than a fault in either map. It is
|
||
|
|
worth recording before a third row arrives, because the cost of two
|
||
|
|
incommensurable axes is small and the cost of five is not.
|
||
|
|
|
||
|
|
flex-auth holds a relevant boundary here, unchanged from 2026-08-19: zone
|
||
|
|
**membership** compiles into the registry snapshot flex-auth already consumes,
|
||
|
|
while per-zone **stance** belongs to the consumer. If zone membership becomes a
|
||
|
|
claim on the decision, `secrets-engine`'s axis can converge onto zones without
|
||
|
|
either side inventing a mapping.
|
||
|
|
|
||
|
|
## Finding 3 — `secrets-engine`'s map cites a shelved artifact
|
||
|
|
|
||
|
|
`pep-stance.yaml` defines `fail_closed` as "no protected side effect without a
|
||
|
|
durable access-engine / `ActionAuthorization` record."
|
||
|
|
|
||
|
|
`ActionAuthorization` was shelved on the PEP consumption path by
|
||
|
|
`GH-DEC-2026-005`, which flex-auth accepted in `FLEX-DEC-2026-006` — arguing
|
||
|
|
against its own proposal. The step-1 artifact is `approval-engine`'s
|
||
|
|
approval-claim; the step-2 artifact is flex-auth's `DecisionEnvelope`.
|
||
|
|
|
||
|
|
The stance itself is unaffected: `fail_closed` still means no protected side
|
||
|
|
effect without a durable record, and the records exist. Only the name of the
|
||
|
|
artifact is stale. Because this file is read at runtime and pinned by test, the
|
||
|
|
stale name is more durable than a comment would be, which is why it is worth a
|
||
|
|
line rather than being left.
|
||
|
|
|
||
|
|
## What flex-auth is not claiming
|
||
|
|
|
||
|
|
- No stance is wrong. flex-auth cannot express fail-open at all, and does not
|
||
|
|
get to grade a consumer's residue.
|
||
|
|
- No change is requested of `ops-warden`. Its `unknown: fail_open` is
|
||
|
|
documented, versioned, and reasoned.
|
||
|
|
- This is not a §13.1 register. gate-house owns the register; this is one
|
||
|
|
reviewer's reading of the two rows now in it.
|