Close the v0.8 assent round: two rulings, nine corrections, one decline

Four repositories returned text reviews. Every substantive finding was
about a rule that read as satisfied by a check that did not satisfy it,
which is the failure mode this repo is structurally prone to: doctrine
is graded on whether it is right and consumed on whether it is
checkable, and only the implementers can tell those apart.

GH-DEC-2026-010 — attribution is not identity. Obligation 1 said a PEP
must hold a decision from access-engine; obligation 2 supplied a digest
test emphatic that it was mechanical rather than a matter of judgement.
That test establishes which request a decision is for and nothing about
who issued it, and it cannot: every input to it is either sent by the
caller or published, so a responder knowing a published package id and
version returns a well-formed allow. Fail-closed protects against a
decision point that is absent, not against one that lies. Section 9.4
required authenticated entries of the approval object and nothing
required it of the decision, so obligation 5 was written over a pair a
PEP could only half validate. The mechanism is access-engine's under
section 17 and it is not the standard's to choose, so the condition is
a declared section 13 gap rather than a rule invented here. Raised by
access-engine against its own artifact, which had already recorded it
as its own defect before reading our text.

GH-DEC-2026-011 — ops-warden assented to GH-DEC-2026-009 on the
falsifier's own terms, went looking for the section 5.1 escape hatch
the reversal clause predicted, and reported it does not have one. Then
it priced adoption: 0 of 3 signing targets and 3 of 21 routing lanes
resolve to a zone, so the cell adopted today fails closed on nearly
every certificate it issues whenever the engine is unreachable —
including the continuity path an operator needs to repair that
unreachability. Its ask for a dated transitional unknown: fail_open is
declined; it is indistinguishable at runtime from the stance the rule
forbids and would make the rule optional at the only moment it costs
anything. Its second preference is adopted instead: 13.1 records a
dated coverage figure beside each stance, so a strict consumer and an
unclassified one stop reading alike. Coverage is disclosure and does
not soften the stance — the record says so, and says what would make
the column come out again.

The round record is closed and carries the rest: totality by catch-all,
absent versus unknown (closing the section 16 question this version
opened), the drift test promoted to MUST, ops-mason marked, and
approval-engine's four editorial-but-load-bearing findings. Its own
finding ids are used rather than renumbered.

kings-guard and audit-core did not return a review. Section 14 records
that as not claimed rather than counting silence as assent, and names
the sections that therefore carry no assent from the repository best
placed to test them.

Standard amended at net-kingdom@64394e9; it stays proposed, and
publication and the acceptance flip are net-kingdom's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012viPor8WJNCbV64ipwewrm

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1754332@bnt-lap001
Assistant-Session: 9c8ac536-ff5e-46a3-8ab1-a548bde25fc0
This commit is contained in:
tegwick 2026-09-09 20:15:12 +02:00
parent a1a3294a6f
commit ddc1337a63
2 changed files with 533 additions and 11 deletions

View file

@ -1295,3 +1295,333 @@ was missing, and that combination is only survivable because the repositories
implementing it read it against their own fixtures rather than accepting it. The
defect was found in hours by the repository that would have been broken by it, and
reported by two others who had no obligation to look.
## GH-DEC-2026-010 — A PEP must be able to attribute a decision to access-engine; the digest test does not do it
```yaml
id: GH-DEC-2026-010
kind: decision
title: A PEP must be able to attribute a decision to access-engine; the digest test
does not do it
status: resolved
owner: Bernd Worsch
repo: gate-house
standard: net-kingdom/canon/standards/security-layer-model_v0.8.md
source_note: flex-auth FLEX-DEC-2026-010; approval-engine docs/reviews/2026-09-07-security-layer-model-v08.md
requested_dispositions:
- approved
- revised
- rejected
affects:
- gate-house
- net-kingdom
- access-engine
- approval-engine
- secrets-engine
- ops-warden
- ops-mason
- user-engine
- tenant-engine
- audit-core
decided_by: Bernd Worsch
rationale: 'Raised by access-engine against its own artifact during the v0.8 assent
round, having recorded it as its own defect (FLEX-DEC-2026-010) before reviewing
this text. Section 6.4 obligation 1 required a PEP to hold a decision from access-engine,
and obligation 2 supplied a test emphatic that it was mechanical rather than a matter
of implementer judgement. That test establishes which request a decision was rendered
for and nothing about who rendered it, and it cannot: every input to it is either
sent by the caller or published, so a responder knowing a published package id and
version reproduces all of it and returns a well-formed allow. Fail-closed protects
against a decision point that is absent, not against one that lies. The asymmetry
with obligation 5 is the sharp form, since section 9.4 requires the approval object
to have authenticated entries and nothing required it of the decision, leaving an
obligation written over a pair of artifacts satisfiable for only one half. The obligation
now requires attribution and states that the digest comparison does not discharge
it; the mechanism is the owner s under section 17 and the gap is declared in section
13 rather than papered by a rule the standard does not get to choose.'
created: '2026-09-09T18:11:20.071372Z'
updated: '2026-09-09T18:11:20.071372Z'
```
## Context
`access-engine` returned its v0.8 review on 2026-09-07 and ranked this finding
blocking for calling §6.4's PEP obligations complete — explicitly **not** blocking
anyone's adoption, since the condition already exists under v0.7 and v0.8 does not
create it. It had already recorded the defect against its own artifact as
`FLEX-DEC-2026-010`, with `FLEX-WP-0024` carrying the fix, before it read this text.
The finding is that the standard is not merely silent. Obligation 1 says a PEP must
hold *"a decision from `access-engine` identifying the request it was rendered for"*,
and obligation 2 supplies a test and is emphatic that it is not a judgement call:
replay is permitted iff the canonical request digest matches and the lifetime holds,
*"mechanical, not a matter of implementer judgement"*. A careful implementer reads the
mechanical test as discharging the obligation above it. It does not.
Every input to that comparison is either sent by the caller or published by the PDP.
The request material is what the PEP transmitted; `policy_package_digest` and
`registry_snapshot_digest` are computable from files in a public repository. A
responder that knows the package id and version — both published — reproduces all
three and returns a well-formed allow. Publishing more digests makes a forged envelope
look **more** authenticated, not less.
`secrets-engine` put the consequence better than either of us: its posture *"silently
assumes the PDP is the PDP"*. `approval-engine` carried it into the correspondence
chain — an approval whose `pdp_digest` matches a **forged** decision matches perfectly.
Identity comparison proves two artifacts describe one request. It proves nothing about
whether the decision is authentic.
**The gap was visible from inside and was recorded against the wrong artifact.** §16
already carried *"publication integrity of the Taxonomy layer itself… while its own
publication path has no digest, freeze, or rollback discipline"*. That is this
observation one layer up. Applied to the artifact the standard regulates rather than to
the standard, it is this decision.
## Decision
### 1. Attribution is required, and is a separate property from identity
**A PEP MUST hold a decision that is *attributable to* `access-engine`, not merely
present and well-formed.** Obligation 2's digest test establishes **which request** a
decision was rendered for. It establishes nothing about **who rendered it**.
**Obligation 1 is not discharged by a digest comparison**, and an implementer MUST NOT
read obligation 2's mechanical test as discharging it. This is the whole of the change:
the obligation stops reading as satisfied by a check that does not satisfy it.
### 2. Fail-closed does not reach this
§9.3's apparatus — two failure cases, two owners, published stance maps — addresses
**absence**. An unreachable PDP denies. A responder impersonating one allows. Nothing
in the standard addressed a responder, and no stance value can: the stance is consulted
when the engine is unreachable, and a lying responder is reachable by construction.
### 3. Obligation 5 was written over a pair it could only half satisfy
§9.4 requires the approval object to have *"durable, authenticated entries"*. §6.4
obligation 5 then requires that where a PEP's decision rests on more than one artifact,
each MUST be validated against the layer that owns its data — and names the
approval-claim / `DecisionEnvelope` pair as the live instance.
So the standard required authenticity of the **PIP's** artifact and not of the
**PDP's**, and wrote an obligation across both. That asymmetry was argued nowhere. It
is oversight, not position, and it is corrected here rather than defended.
### 4. The mechanism is not the standard's to choose
`access-engine` did not ask for a signature scheme in the statute, and it does not get
one. The shape is the owner's under §17. What the standard names is the property.
Until a mechanism ships, the condition is a **declared §13 gap** with `access-engine`
as owner (`FLEX-DEC-2026-010`, `FLEX-WP-0024`; `FLEX-DEC-2026-009` puts the
authenticated caller in provenance, so a record eventually attests both ends of the
channel rather than neither). A declared gap is honest. An unstated assumption inside a
test called mechanical is not.
## What this does not do
It does not close the gap. `access-engine`'s note that Glas completed operator
caller-auth mid-review is correctly self-limited: an authenticated API-server
port-forward authenticates the responder for one operator path and produces no signed
portable artifact.
It also does not create an adoption blocker. The condition predates v0.8 and every
PEP-shaped consumer is in it today. What changes is that they can now see they are.
## Reversal condition
If a PEP can verify the decision's origin by a mechanism that does not itself rest on
material the caller supplies or the PDP publishes openly, the §13 gap closes and this
record's second half becomes historical. The first half — that identity is not
attribution — does not revert; it is a property of hashing, not of the current channel.
## Provenance
Raised by `access-engine` (`FLEX-DEC-2026-011`, v0.8 assent, F1), against its own
artifact and having recorded the defect on its own side first. Independently framed by
`secrets-engine`. Carried into the correspondence chain by `approval-engine`, which
noted its own `docs/approval-claim.md` said what `pdp_digest` cannot cover without
saying it cannot detect a forged decision. Applied to
`security-layer-model_v0.8.md` §6.4 obligation 1 and §13 at `net-kingdom@64394e9`.
## GH-DEC-2026-011 — Classification coverage is published beside the stance; a transitional fail_open is declined
```yaml
id: GH-DEC-2026-011
kind: decision
title: Classification coverage is published beside the stance; a transitional fail_open
is declined
status: resolved
owner: Bernd Worsch
repo: gate-house
standard: net-kingdom/canon/standards/security-layer-model_v0.8.md
source_note: ops-warden history/2026-09-09-layer-model-v08-review.md @ 8e1b621; flex-auth
FLEX-DEC-2026-011 F2
requested_dispositions:
- approved
- revised
- rejected
affects:
- gate-house
- net-kingdom
- ops-warden
- ops-mason
- secrets-engine
- user-engine
- tenant-engine
- access-engine
- zone-engine
decided_by: Bernd Worsch
rationale: 'ops-warden assented to GH-DEC-2026-009 on its own terms and then reported
what adopting it costs: zero of three signing targets and three of twenty-one routing
lanes resolve to a zone, so unknown fail_closed adopted today would fail closed
on essentially every certificate it issues whenever access-engine is unreachable,
including the continuity path an operator needs to repair that unreachability. It
asked for a dated transitional unknown fail_open converting on coverage rather than
calendar. Declined: a sanctioned transitional fail_open is indistinguishable at
runtime from the stance the rule forbids and would make the rule optional at the
only moment it costs anything, while section 11 s declared-gap mark already expresses
a correct rule whose adoption is not yet affordable. Its second preference is adopted
instead. Section 13.1 records a dated classification-coverage figure beside each
stance, because unknown fail_closed over an entirely unclassified population is
conformant and materially misleading. access-engine reached the same place from
the other side: totality satisfied by a catch-all is satisfied vacuously, so a map
must enumerate its axis and an absent scope must be distinguishable in the record
from an unknown one and surface as a conformance failure.'
created: '2026-09-09T18:12:02.553370Z'
updated: '2026-09-09T18:12:02.553370Z'
```
## Context
`GH-DEC-2026-009` made `unknown``fail_closed` normative and made one cell of
`ops-warden`'s published stance map non-conformant. That ruling was circulated with
v0.8 rather than imposed, precisely because the repository bearing its cost had not
reviewed it. `ops-warden` returned the review on 2026-09-09
(`history/2026-09-09-layer-model-v08-review.md`, `8e1b621`).
**It assented to the rule, on the falsifier's own terms.** The reversal clause
anticipated that such a path would be a §5.1 read-only diagnostic carrying no PEP
obligation. `ops-warden` went looking for that escape hatch on its own side and
reported that it does not have one: the stance map governs exactly one protected
action, `warden sign`, which is a credential-issuing side effect that §5.1 does not
reach. The predicted defence is absent and the argument stands unrebutted.
**Then it priced the adoption.** Measured 2026-09-09:
```text
signing targets (actor resources in snapshot): 0 resolved, 3 unknown, 1 n/a
routing catalog lanes: 3 resolved, 18 unknown, 12 n/a
```
Zero of its signing targets resolve to a zone. So `unknown``fail_closed` adopted
today does not fail closed on an edge case; it fails closed on essentially every
certificate `ops-warden` issues, whenever `access-engine` is unreachable. That is the
configuration `ADR-0006` rejected, reached by another route: one value making the
decision engine a uniform dependency of every signing path, **including the continuity
path needed to repair that dependency**. Engine down → operator needs an SSH
certificate to reach the host and restore it → the repair target is unknown because
nobody classified it → denied.
Eighteen of its eighteen unknown lanes are unknown because **another** repository has
not published a workload-identity declaration, and `ADR-0009` rule 3 forbids closing
that by inference.
`access-engine` arrived at the same register from the opposite side (`FLEX-DEC-2026-011`,
F2): a map carrying `unknown``fail_closed` satisfies obligation 3's totality
requirement **vacuously**. Every scope the author never enumerated lands in `unknown`,
fails closed, and nobody ever learns which those were. It recognised the shape because
it published it — `FLEX-DEC-2026-008`, a policy package shipped with no tenant rule at
all while 29 fixtures passed, because every fixture carried the same tenant.
## Decision
### 1. A dated transitional `unknown: fail_open` is declined
`ops-warden`'s first preference was that obligation 3 name a transition: a consumer may
declare `unknown: fail_open` as a dated, published transitional state with a coverage
figure attached, converting on coverage rather than on calendar.
**Declined.** A sanctioned transitional `fail_open` is indistinguishable at runtime from
the stance the rule forbids. It makes the rule optional at the moment of adoption — the
only moment it costs anything — and `unknown` is the cheapest state for an attacker to
induce whether or not the consumer has dated its intention to stop being permissive.
The mechanism asked for already exists and does not have that defect: §11's
**declared-gap** mark says *"correct rule, adoption not yet affordable"* without
inverting the rule's effect. `ops-warden` proposed exactly that as its own second
preference and named `WARDEN-WP-0040` as the route.
The deadlock is real and the answer is classification, not a permissive axis: continuity
paths are classified into a scope whose stance is open, and the deadlock does not arise.
`ops-warden` accepted that answer and correctly observed that it is work not yet done.
Work not yet done is a gap. It is not a reason to hold the axis open.
**And a stricter stance is not a licence to manufacture the membership that makes it
survivable.** `ADR-0009` rule 3 is right and must hold under pressure from this ruling.
A consumer MUST NOT infer a scope another repository has not declared.
### 2. Classification coverage is published beside the stance
**§13.1 records a dated classification-coverage figure for each consumer.** A row
reading `unknown``fail_closed` while 100% of that consumer's targets are unknown is
conformant and materially misleading: a reader cannot distinguish a strict consumer from
an unclassified one. It is §11's published-map-equals-shipped-behaviour rule one level
up — the map becomes accurate about itself and inaccurate about its effect.
Coverage is **disclosure, not a licence**. It does not soften the stance, gate it, or
create a state in which a non-conformant cell becomes conformant. It reports.
Coverage is reported by the consumer and never computed here. A blank means *"not
reported"*, never *"complete"*.
### 3. A map must enumerate its axis; `absent` is not `unknown`
**Totality MUST NOT be satisfied by a catch-all.** A published map enumerates the axis
it scopes over. Otherwise obligation 3's totality requirement is unfalsifiable and its
drift test passes by exercising the default rather than the axis.
**`unknown` and `absent` are one runtime behaviour and two meanings.** Both fail closed
`absent` for the stronger reason, since it is the branch reached by discovering that
the author's model of their own axis was wrong, and §8's asymmetry forbids being more
permissive on surprise. But an `unknown` hit is normal operation under a considered
stance, while an `absent` hit is evidence this obligation is violated. **An `absent` hit
MUST be distinguishable in the record and MUST surface as a conformance failure** rather
than be absorbed by the catch-all.
This closes the question §16 opened at the v0.8 cut.
### 4. `ops-mason` is marked
An unpublished stance map is a plainer violation of obligation 3 than a wrongly-valued
cell in a published one, and §13.1 stated it as bare fact while bolding the other. A
reader scanning for marks found one row and concluded the other four were fine. That is
Gate House's own §11 marking obligation applied to Gate House's own register, returned
unchanged by `access-engine` after receiving the same argument about a stale row of its
own. `ops-mason`'s row is now marked, with no route recorded.
## Reversal condition
If a consumer demonstrates a scope that is genuinely unclassifiable, genuinely
low-consequence, and whose permissive resolution cannot be induced by an unprivileged
party, §1 is open to revision — that falsifier is `GH-DEC-2026-009`'s and survives here.
`ops-warden` went looking for it and reported its absence, which is evidence and not
proof.
§2 reverses if coverage figures start being read as a stance qualifier rather than as
disclosure. If a row is ever argued to be conformant *because* its coverage is low, the
column is doing harm and comes out.
## Provenance
Raised by `ops-warden` (v0.8 assent, two findings and one agreement), which bears the
entire cost of the ruling it assented to and asked for the transition it did not get.
Its measured figures are the first entries in the coverage column. §3 raised by
`access-engine` (`FLEX-DEC-2026-011`, F2); §4 by `access-engine` (F4). Applied to
`security-layer-model_v0.8.md` §6.4 obligation 3 and §13.1 at `net-kingdom@64394e9`.
`access-engine` added, unprompted, that its concurrence with `GH-DEC-2026-009` cost it
nothing — it is not PEP-shaped and publishes no stance map — and that a PDP's assent to
a ruling falling entirely on other repositories is weak evidence for it. That is
recorded because it is right: the strength here is the asymmetry argument and
`ops-warden`'s assent against its own interest, not the count of repositories agreeing.