Collect v0.8 assent findings; correct GH-DEC-2026-008 for implementability
Four findings returned so far, all from access-engine and approval-engine. F1 is the serious one: GH-DEC-2026-008 as written mandated a comparison that could never pass, because a claim travelling inside a hashed request cannot name the digest of the request containing it. A fail-closed consumer obeying it would have denied destroy permanently — the ruling and its own fail-closed requirement compounded rather than cancelled. Ruling and its four obligations stand; the comparison target is corrected to the PDP's published exclusion-scoped digest, verified in flex-auth's schema and canonical.go before amending. A consumer must not guess the exclusion rule, and until a PDP publishes one the path is fail-closed rather than complete. F2 adds the general property access-engine flagged as a near miss it was not asking to have written: an evidence-bearing input may be excluded from a correspondence digest but never from the replay identity. The round record notes what this says about the process. GH-DEC-2026-008 was correct in substance, argued from doctrine, and verified against another repository's schema before issue — and none of that caught a defect three repositories found within hours of building on it. 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
6f4bd56ee5
commit
d97e8efa6e
3 changed files with 209 additions and 5 deletions
|
|
@ -1219,3 +1219,79 @@ 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.
|
||||
|
||||
---
|
||||
|
||||
### Amendment to GH-DEC-2026-008, 2026-09-06 — the comparison as written could never pass
|
||||
|
||||
Found by `secrets-engine` re-verifying a replay fixture within hours of the ruling,
|
||||
and reported independently by `access-engine` and `approval-engine`. Verified here
|
||||
against `flex-auth/schemas/decision_envelope.schema.json` and `pkg/api/canonical.go`
|
||||
before amending.
|
||||
|
||||
**The defect.** The ruling required `claim.binding.pdp_digest` to equal the decision's
|
||||
`NewDecisionBinding.request_digest`. `access-engine` hashes context into the request
|
||||
digest, and the dual-control pattern carries the claim in `context.approval`.
|
||||
Embedding the claim therefore changes the digest of the request that carries it, so a
|
||||
digest recorded at issue can never equal the final one.
|
||||
|
||||
This is a **hash cycle**, and `approval-engine` put the resolution correctly: it is
|
||||
forced rather than chosen. A claim cannot carry the digest of a document containing
|
||||
that claim — the value would have to be known before it could be computed. The
|
||||
recorded digest is necessarily of the underlying action *before* any claim was
|
||||
embedded.
|
||||
|
||||
**The consequence had it shipped.** A consumer obeying the rule and failing closed
|
||||
would have denied `destroy` permanently — failing closed forever on a check that
|
||||
cannot pass. The ruling's own fail-closed requirement is what would have made it
|
||||
harmful rather than merely wrong.
|
||||
|
||||
**The fix, and whose it was.** `access-engine` publishes
|
||||
`binding.approval_binding_digest` — the same canonical material with
|
||||
`context.approval` removed, emitted only where a claim was carried
|
||||
(`FLEX-DEC-2026-007`). An approval issued against a claim-free `Check` records that
|
||||
`Check`'s digest, and the claim-bearing request reproduces it. The comparison becomes
|
||||
implementable without changing what it requires: correspondence by identity, no
|
||||
translation, nobody asserting an equivalence between vocabularies. **The A5 property
|
||||
holds exactly as drafted.**
|
||||
|
||||
`approval-engine` was right that what was missing was a published statement of which
|
||||
fields the PDP excludes, and right not to make that statement on `access-engine`'s
|
||||
behalf. **A consumer MUST NOT guess the exclusion rule**: a digest computed under an
|
||||
assumed rule produces a confident wrong answer, and comparing two digests derived
|
||||
under different rules fails open toward accepting a claim bound to a different
|
||||
request — the same failure direction as the vocabulary mapping this decision rejected,
|
||||
reached by another road. Until a PDP publishes its exclusion rule, the path is
|
||||
correctly fail-closed rather than complete: a gap in a dependency, not in the ruling.
|
||||
|
||||
**What this changes in the record.** The ruling stands. Its four numbered obligations
|
||||
stand. Obligation 1's comparison target is corrected from the request digest to the
|
||||
PDP's published exclusion-scoped digest. v0.8 §9.7.3 carries the corrected form.
|
||||
|
||||
**And a general property, flagged by `access-engine` as a near miss it does not ask
|
||||
for.** The obvious fix is to drop `context.approval` from the request digest
|
||||
entirely. That is unsafe, and the reason generalises: **an evidence-bearing input may
|
||||
be excluded from a correspondence digest but never from the replay identity.** Two
|
||||
requests differing only in which approval was presented decide differently — one
|
||||
allows, one denies `dual_control_required` — so collapsing them would let an allow
|
||||
obtained with a valid claim be replayed against a request carrying none. A fail-open
|
||||
hole reached by a refactor that looks like simplification. It is in v0.8 §6.4
|
||||
obligation 5 because the shape recurs wherever evidence travels inside a hashed
|
||||
request, and because a property discovered by nearly getting it wrong is worth more
|
||||
written down than remembered.
|
||||
|
||||
**Implementation status.** `access-engine`: `FLEX-DEC-2026-007`, `dd3ce4c`.
|
||||
`approval-engine`: schema v3 adds a declared `pdp_path`; `create()` refuses
|
||||
`pdp_path: true` without a `pdp_digest`, so an approval that would be unusable on the
|
||||
path fails **at issue** rather than at the protected side effect. Intent is declared,
|
||||
never inferred — a digest that happens to be present is not a declaration anybody
|
||||
made, legacy rows migrate to `false` rather than being back-filled, and a successor
|
||||
inherits its predecessor's declaration. Back-filling would have manufactured a
|
||||
statement no requester made, which is this decision's own objection to translation in
|
||||
another form.
|
||||
|
||||
**What this says about the ruling process.** The ruling was right and its mechanism
|
||||
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.
|
||||
|
|
|
|||
112
docs/conformance/2026-09-06-v08-assent-round.md
Normal file
112
docs/conformance/2026-09-06-v08-assent-round.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# v0.8 assent round — findings and dispositions
|
||||
|
||||
**Repository:** gate-house
|
||||
**Project family:** NetKingdom security layer
|
||||
**Status:** open — round in progress
|
||||
**Version:** 0.1
|
||||
**Date:** 2026-09-06
|
||||
**Subject:** `net-kingdom/canon/standards/security-layer-model_v0.8.md` (proposed)
|
||||
**Workplan:** `GH-WP-0003-T06`
|
||||
|
||||
*Derived artifact: this record states the round's status as of the date above, not
|
||||
current state. Authoritative bodies are the decision records and the cut itself.*
|
||||
|
||||
## Circulated
|
||||
|
||||
`ops-warden`, `approval-engine`, `access-engine`, `kings-guard`, `audit-core`, and
|
||||
`net-kingdom` (publisher). Each was told what the version costs them and what to
|
||||
attack, rather than sent a summary to nod at.
|
||||
|
||||
## Findings returned
|
||||
|
||||
### F1 — `GH-DEC-2026-008` was unimplementable as written · **adopted, corrected**
|
||||
|
||||
Reported by `access-engine`, independently by `approval-engine`, originally found by
|
||||
`secrets-engine` re-verifying a replay fixture — within hours of the ruling and before
|
||||
the assent round had properly opened.
|
||||
|
||||
The ruling required `claim.binding.pdp_digest` to equal the decision's
|
||||
`NewDecisionBinding.request_digest`. `access-engine` hashes context into that digest
|
||||
and the dual-control pattern carries the claim in `context.approval`, so embedding the
|
||||
claim changes the digest of the request carrying it. A hash cycle: a claim cannot name
|
||||
the digest of a document containing that claim.
|
||||
|
||||
**A fail-closed consumer obeying the rule would have denied `destroy` permanently.**
|
||||
The ruling's own fail-closed requirement is what would have made it harmful rather
|
||||
than merely wrong — the rule and the failure mode compounded instead of cancelling.
|
||||
|
||||
**Disposition.** Ruling stands; its four obligations stand; obligation 1's comparison
|
||||
target is corrected to the PDP's published exclusion-scoped digest
|
||||
(`binding.approval_binding_digest`, `FLEX-DEC-2026-007`). v0.8 §9.7.3 rewritten. A
|
||||
consumer MUST NOT guess the exclusion rule, and until a PDP publishes one the path is
|
||||
fail-closed rather than complete — a gap in a dependency, not in the ruling.
|
||||
|
||||
Verified independently against `flex-auth/schemas/decision_envelope.schema.json` and
|
||||
`pkg/api/canonical.go` before amending.
|
||||
|
||||
### F2 — replay identity versus correspondence digest · **adopted, added**
|
||||
|
||||
Flagged by `access-engine` explicitly as a near miss it was *not* asking to have
|
||||
written down. The obvious fix to F1 is to drop `context.approval` from the request
|
||||
digest entirely, and that is unsafe: two requests differing only in which approval was
|
||||
presented decide differently, so collapsing them lets an allow obtained with a valid
|
||||
claim be replayed against a request carrying none.
|
||||
|
||||
**An evidence-bearing input may be excluded from a correspondence digest but never
|
||||
from the replay identity.** Added to v0.8 §6.4 obligation 5. A property discovered by
|
||||
nearly getting it wrong is worth more written down than remembered, and the shape
|
||||
recurs wherever evidence travels inside a hashed request.
|
||||
|
||||
### F3 — `GH-DEC-2026-009` adopted, with a reason we had not stated
|
||||
|
||||
`access-engine` records that the `unknown` ruling is right for the adversarial reason
|
||||
— `unknown` is the cheapest state to induce, so failing open on it makes being
|
||||
unclassifiable a credential-free escalation — and notes it had reported the divergence
|
||||
without reaching that reading. No change; recorded because the reasoning, not the
|
||||
conclusion, is what a later reader needs.
|
||||
|
||||
It also endorses sending `ops-warden`'s non-conformant cell to the round rather than
|
||||
imposing it.
|
||||
|
||||
### F4 — A7 accepted by its own worked example
|
||||
|
||||
`access-engine` accepts its dual-control instance as the one carrying the argument,
|
||||
and notes the §11 marking obligation would have caught the source: the stale G3 row in
|
||||
its own 2026-08-29 review, handed on as current. No change.
|
||||
|
||||
## Implementation ahead of acceptance
|
||||
|
||||
Both engines implemented against the ruling before the round closed.
|
||||
|
||||
- `access-engine` — `FLEX-DEC-2026-007`, `dd3ce4c`: publishes
|
||||
`binding.approval_binding_digest`, and asserts by test that the two digests
|
||||
**disagree** on a claim-bearing request, so a later refactor cannot collapse them.
|
||||
- `approval-engine` — `7e75677`, 102 tests: schema v3 declares `pdp_path`; `create()`
|
||||
refuses `pdp_path: true` without a `pdp_digest`, so an approval that would be
|
||||
unusable on the path fails **at issue** rather than at the protected side effect.
|
||||
Intent is declared and never inferred — a digest that happens to be present is not a
|
||||
declaration, legacy rows migrate to `false` rather than being back-filled, and a
|
||||
successor inherits its predecessor's declaration. Back-filling would have
|
||||
manufactured a statement no requester made, which is `GH-DEC-2026-008`'s own
|
||||
objection to translation in another form.
|
||||
|
||||
## Still outstanding
|
||||
|
||||
`ops-warden` (the `unknown` cell it acquires), `kings-guard` (the criteria-grounding
|
||||
clause, and whether §12's step-four paragraph is now stale), `audit-core` (whether
|
||||
§11's emission-guarantee wording lets a source imply completeness), `net-kingdom`
|
||||
(whether §17 says what it would say in its own voice, and whether §11 tracks its
|
||||
published profile).
|
||||
|
||||
## What the round has shown so far
|
||||
|
||||
The first finding against v0.8 was a rule that was **right and unimplementable**, and
|
||||
it was found by the repository that would have been broken by it, then reported by two
|
||||
others under no obligation to look. That combination is only survivable because the
|
||||
implementers read the ruling against their own fixtures instead of accepting it — the
|
||||
behaviour §12 describes, arriving without being asked for.
|
||||
|
||||
It is also an argument for the round itself. `GH-DEC-2026-008` was correct in
|
||||
substance, argued from doctrine, and verified against another repository's schema
|
||||
before it was issued. None of that caught a defect that three repositories found in
|
||||
hours of trying to build on it.
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
**PDP:** access-engine (currently flex-auth)
|
||||
**Status:** normative for GH-WP-0002-T06; recorded as `GH-DEC-2026-003`,
|
||||
amended by `GH-DEC-2026-005` (step-1 artifact and split validation) and
|
||||
`GH-DEC-2026-008` (the PDP digest is the binding correspondence)
|
||||
`GH-DEC-2026-008` (the binding correspondence, amended for implementability)
|
||||
**Date:** 2026-09-06
|
||||
**Statute:** `net-kingdom/canon/standards/security-layer-model_v0.7.md` §9.4, §9.7.3–4, §16
|
||||
**Blocks:** `APPROVAL-WP-0001-T05`, `FLEX-WP-0017-T05`
|
||||
|
|
@ -61,10 +61,26 @@ claim.
|
|||
holds no runtime approval authority; the issuer to check is the engine that
|
||||
served the claim.
|
||||
|
||||
**The correspondence is the PDP digest.** Amended by `GH-DEC-2026-008`. A consumer
|
||||
on this path MUST verify that `claim.binding.pdp_digest` equals the decision's
|
||||
`NewDecisionBinding.request_digest`, and MUST NOT use a claim that carries no
|
||||
`pdp_digest`. Recomputing `approval-engine`'s native `binding.digest` from a
|
||||
**The correspondence is a digest, and it is the PDP's to define.** Amended by
|
||||
`GH-DEC-2026-008`. A consumer on this path MUST verify that the approval's recorded
|
||||
PDP digest equals the digest the PDP publishes **for the request with the approval
|
||||
evidence excluded** — `binding.approval_binding_digest` in `access-engine` — and MUST
|
||||
NOT use a claim carrying no such digest.
|
||||
|
||||
The comparison is **not** against the full request digest. Where the claim travels
|
||||
inside the hashed request, embedding it changes that request's digest, so a digest
|
||||
recorded at issue can never equal the final one: a hash cycle whose resolution is
|
||||
forced, not chosen. The first form of this rule mandated a check that could never
|
||||
pass, and a fail-closed consumer obeying it would have denied the action permanently.
|
||||
|
||||
A consumer MUST NOT guess the exclusion rule. A digest computed under an assumed rule
|
||||
produces a confident wrong answer, and comparing two digests derived under different
|
||||
rules fails open toward accepting a claim bound to a different request. Until a PDP
|
||||
publishes its exclusion rule, this path is correctly fail-closed rather than complete.
|
||||
|
||||
An **evidence-bearing input may be excluded from a correspondence digest but never
|
||||
from the replay identity** — two requests differing only in which approval was
|
||||
presented decide differently, so one digest cannot serve both purposes. Recomputing `approval-engine`'s native `binding.digest` from a
|
||||
`CheckRequest` is **not** a permitted fallback here: it requires translating between
|
||||
two engines' vocabularies, no mapping is published, and a wrong translation fails
|
||||
open by silently accepting a claim approved for something else. The native limb
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue