Rule on unknown and the scoping axis as GH-DEC-2026-009
access-engine exercised the divergence capability it claimed in the v0.6 round, on the first occasion §13.1 held two rows. ops-warden resolves unknown to fail_open, secrets-engine to fail_closed; both conformant, both total, both test-pinned, disagreeing about the one case that by construction nobody planned for. They also scope over different axes, so the register cannot answer what an inventory exists to answer. Ruling 1: unknown is not a zone and MUST fail closed. §9.3 permits trading availability for openness per zone — and that trade requires knowing the zone. Where the scope is unknown the trade cannot have been made for it, so a permissive unknown does not extend a considered decision, it invents the most permissive one. An unreachable engine is a known request in a degraded system; an unclassified subject is not. unknown is the cheapest state for an attacker to induce, so failing open on it makes being unclassifiable a privilege escalation requiring no credential, which §8's asymmetry forbids wherever it appears. Ruling 2: each map declares its scoping axis and its relation to zone. Forcing everyone onto zones would make secrets-engine assert a zone it cannot know, and a fiction in a runtime-read test-pinned file is worse than an honest incommensurability. The register records the axes and states that cross-axis aggregation is unavailable. ops-warden acquires one non-conformant cell at v0.8. It did everything asked — published first, built the reference form, offered it estate-wide — so this goes to the assent round rather than being imposed quietly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtJBr77gMFLrN93iEevqQJ Assistant: claude-code Assistant-Model: opus Assistant-Process: 425128@bnt-lap001 Assistant-Session: f5944d8b-dac4-4e1a-87eb-8b3d8f314a63
This commit is contained in:
parent
45a65c0748
commit
22915cf2a0
3 changed files with 187 additions and 6 deletions
|
|
@ -1112,3 +1112,110 @@ decided_by: Bernd Worsch
|
|||
created: '2026-09-06T12:17:19.104531Z'
|
||||
updated: '2026-09-06T12:17:19.104531Z'
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
On 2026-08-29 `access-engine` told this repository it was the party positioned to
|
||||
notice when the aggregate of consumer stances diverged from what the policy packages
|
||||
say — and then recorded that the claim was sound but the data was not there, because
|
||||
§13.1 held one row and one row cannot diverge from anything. `secrets-engine`'s
|
||||
publication made it two, and the divergence appeared immediately.
|
||||
|
||||
That is worth noting on its own: the register earned its existence the first time it
|
||||
had two entries.
|
||||
|
||||
**`ops-warden` declares `unknown` → `fail_open`.** Explicit, versioned, under
|
||||
`ADR-0009`. **`secrets-engine` declares `unknown` → `fail_closed`.** Both maps are
|
||||
total, published, test-pinned, and §6.4-conformant. Neither is a defect under v0.7.
|
||||
They disagree about the one case that by construction is the one nobody planned for.
|
||||
|
||||
They are also **not comparable**: `ops-warden` scopes by security zone,
|
||||
`secrets-engine` by catalog stage — explicitly as the "equivalent scope" §6.4 permits,
|
||||
until zone membership arrives as a claim. So the register cannot answer *"what is the
|
||||
estate's stance for a `z2`-protected workload"*, which is the question an inventory
|
||||
exists to answer.
|
||||
|
||||
`access-engine` reported both as observations and explicitly **not** as a request that
|
||||
either repository change its stance, on the ground that a PDP does not set a
|
||||
consumer's stance — §9.3's two-owner split, which was its own finding, cutting against
|
||||
its own convenience. That restraint is correct and it is why the question arrives here:
|
||||
setting a consumer's stance is not the PDP's to do, but doctrine is Gate House's.
|
||||
|
||||
## Decision
|
||||
|
||||
### 1. `unknown` is not a zone, and it fails closed
|
||||
|
||||
**A stance map MUST resolve `unknown` to `fail_closed`.** It is not a scope over which
|
||||
the §9.3 availability trade may be made.
|
||||
|
||||
The reasoning is a distinction v0.7 does not draw, and the two maps are the proof it
|
||||
is needed. §9.3 sanctions trading availability for openness **per zone** — knowingly,
|
||||
for a named scope, at a declared cost. `ops-warden`'s `fail_open` for `z0`–`z2` is
|
||||
exactly that and remains sanctioned: it is a *known* request in a *degraded* system.
|
||||
The consumer knows what it is being asked and cannot reach the engine to ask.
|
||||
|
||||
`unknown` is a different failure. It is not a degraded system; it is an
|
||||
**unclassified subject**. The consumer does not know what it is being asked.
|
||||
|
||||
**A per-zone trade requires knowing the zone.** Where the scope is unknown, the trade
|
||||
cannot have been made *for* it — there was no zone in hand when the profile was
|
||||
written, so no one weighed this request's cost. Resolving `unknown` to the permissive
|
||||
answer therefore does not extend a considered decision; it invents one, and it invents
|
||||
the most permissive one available.
|
||||
|
||||
The adversarial reading settles it. `unknown` is the cheapest state for an attacker to
|
||||
induce: an unregistered workload, a malformed label, a resource created before
|
||||
classification, a race against registry propagation. `fail_open` on `unknown` makes
|
||||
"be unclassifiable" a privilege escalation, and it is the one escalation that requires
|
||||
no credential. This is the §8 asymmetry — nothing may manufacture privilege — applied
|
||||
where the estate had left a gap open by symmetry with a rule that does not reach it.
|
||||
|
||||
`ops-warden`'s map is conformant today and this makes one cell of it non-conformant at
|
||||
v0.8. That is a doctrine change with a cost to a repository that did everything asked
|
||||
of it — published early, built the reference form, and offered it estate-wide — and it
|
||||
should be argued in the assent round rather than imposed quietly. Their `z0`–`z2`
|
||||
zone stances are untouched.
|
||||
|
||||
### 2. A stance map declares its scoping axis and its relation to zone
|
||||
|
||||
**Each published map MUST name the axis it scopes over, and state its relationship to
|
||||
security zone: a mapping, or an explicit declaration that none exists yet and why.**
|
||||
|
||||
The register is not repaired by forcing everyone onto zones. `secrets-engine` chose
|
||||
catalog stage because zone membership is not yet available to it as a claim; making it
|
||||
assert zones now would produce a fiction, and a fiction in a runtime-read,
|
||||
test-pinned file is worse than an honest incommensurability.
|
||||
|
||||
**The register instead states what it cannot answer.** §13.1 carries the axis per row
|
||||
and records that cross-axis aggregation is not available. An inventory that silently
|
||||
cannot answer its own question is the §9.1 defect again — a surface catalogued without
|
||||
the capability behind it.
|
||||
|
||||
`access-engine` notes the convergence path and it is the right one: zone *membership*
|
||||
compiles into the registry snapshot it already consumes, while per-zone *stance*
|
||||
belongs to the consumer. If membership reaches the decision as a claim,
|
||||
`secrets-engine`'s axis converges onto zones with neither side inventing a mapping.
|
||||
Until then the axes are recorded, not reconciled.
|
||||
|
||||
The cost of two incommensurable axes is small; the cost of five is not. This lands
|
||||
before the third row rather than after the fifth.
|
||||
|
||||
### What is not decided here
|
||||
|
||||
Neither repository is asked to change its zone stances. `secrets-engine`'s stale
|
||||
reference to a durable `ActionAuthorization` record in its map — the artifact
|
||||
`GH-DEC-2026-005` shelved — is theirs to correct and `access-engine` has already told
|
||||
them; it is a naming error in a runtime-read file, where a stale name outlives a stale
|
||||
comment, and it does not change the stance.
|
||||
|
||||
## Reversal
|
||||
|
||||
Reverse ruling 1 if a consumer demonstrates a scope that is genuinely unknown *and*
|
||||
genuinely low-consequence, where failing closed on it costs availability with no
|
||||
security gain — the plausible candidate is a read-only diagnostic path under §5.1. The
|
||||
expected resolution is that such a path is not a protected side effect and therefore
|
||||
has no PEP obligation at all, rather than that it needs a permissive `unknown`. If
|
||||
that resolution fails on a real path, this ruling is too broad.
|
||||
|
||||
Reverse ruling 2 if declaring the axis proves to be all the register needed, in which
|
||||
case the "states what it cannot answer" half is redundant rather than wrong.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue