gate-house/docs/amendments/v0.8-amendment-set.md
tegwick 6f4bd56ee5 Assemble v0.8 and open the assent round (T06)
The cut is at net-kingdom@66eeaba as status: proposed. All eight
amendments applied, with a §15 change-log entry, a §1 "what changed", a
§16 reconciliation closing two questions and opening one, and a rewritten
§14. Section numbering unchanged.

§14 no longer claims acceptance on the owner's decision. Ten of eleven
changes were requested by another repository and seven by a repository
arguing against its own interest, but this version imposes costs on named
repositories — ops-warden's unknown cell, approval-engine's issue-time
digest — and a cost imposed without a review round is what §12 exists to
catch late. So it circulates first.

The amendment set stays as the per-amendment argument; the cut is the
text under review. v0.7 remains accepted and unpatched.

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
2026-09-06 14:53:09 +02:00

441 lines
24 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.

# Security layer model — v0.8 amendment set
**Repository:** gate-house (author)
**Publisher:** net-kingdom
**Project family:** NetKingdom security layer
**Status:** cut — assembled into `net-kingdom/canon/standards/security-layer-model_v0.8.md` (proposed) at `net-kingdom@66eeaba`; circulating for assent
**Version:** 1.0
**Date:** 2026-09-06
**Workplan:** `GH-WP-0003`
**Base:** `net-kingdom/canon/standards/security-layer-model_v0.7.md` (accepted)
## What this document is
The exact normative text for the amendments `GH-WP-0003` collects, section by
section, against accepted v0.7. It is the reviewable unit: each amendment can be
argued on its own wording here before any of them touch a cut document.
v0.7 is **not** patched in place. Nothing in this file is in force. Each amendment
already governs its implementers through the decision record or contract named as its
authority — this document moves them into the statute, it does not decide them again.
**Assembled.** All eight amendments are in the v0.8 cut, together with a §15
change-log entry, a §1 "what changed", a §16 reconciliation against the decision
log, and a rewritten §14. Section numbering is unchanged — the estate cites it. This
document remains the per-amendment argument; the cut is the text under review.
| # | Section | Authority | Task |
| --- | --- | --- | --- |
| A1 | §9.7.3 | `GH-DEC-2026-003`, `docs/contracts/approval-consumption.md` | T01 |
| A2 | §11 | `docs/contracts/approval-emission-detection.md` | T02 |
| A3 | §13, §13.1 | `GH-DEC-2026-006` | T03 |
| A4 | §9.5 | `GH-DEC-2026-007` | T04 |
| A5 | §6.4, §8 | `GH-DEC-2026-005`, `GH-DEC-2026-008` | T05 |
| A6 | §17 | `GH-DEC-2026-004` | T07 |
| A7 | §11, §12 | six observed instances; proposed by `approval-engine` | T08 |
| A8 | §6.4, §13.1 | `GH-DEC-2026-009` | T09 |
---
## A1 — §9.7.3, consume ordering (T01)
**Defect.** v0.7 item 3 reads *"the decision precedes the action and the action
precedes consumption"*. That clause is a protocol, and it is the wrong one. It was
written to support a forensic claim, not to specify an order, but implementers read
it as the order — `APPROVAL-WP-0001-T05` and `FLEX-WP-0017-T05` were both blocked on
it.
**Why the stated order is unsafe.** If the PEP acts and then consumes, two racing
PEPs can both observe `valid_now`, both receive ALLOW, and both act. The CAS then
prevents only the second *record*, not the second *side effect*. Single consumption
becomes theatre — the property the mutation exists to provide is exactly the one the
ordering destroys.
**Replace item 3 with:**
> 3. **Consumption is a state change, never an inference.** An approval is consumed
> by a mutation in `approval-engine` (§9.4). It MUST NOT be inferred from the
> existence of a decision record: a decision record proves an intent to act, not
> an act.
>
> **The PEP MUST obtain a successful consume before the protected side effect.**
> Holding a claim with `valid_now: true`, or an ALLOW rendered against that claim,
> is not authority to act; the consume is. In-flight duplicate ALLOWs are expected
> and the CAS serializes use — a later `CheckRequest` sees `valid_now: false` and
> cannot mint a new ALLOW against the same object.
>
> v0.7 stated the opposite order. Acting first leaves the CAS able to prevent only
> the second *record*, never the second *side effect*, which makes single
> consumption theatre. This is a protocol correction and not a retraction of the
> forensic claim: consumption is still never inferred from a decision.
>
> An approval authorizes one **attempt**, not one success. There is no unconsume
> and no reserve/release; a consumed approval spent on a failed action is spent.
> Reversibility would reopen replay, which is the failure the mutation exists to
> close. Settled in `GH-DEC-2026-003`; the protocol is
> `gate-house/docs/contracts/approval-consumption.md`.
**Item 4 stays as written.** Its three failure modes now have owners rather than only
names, in the same contract; the statute continues to name them and to point at it.
---
## A2 — §11, the emission-guarantee declaration check (T02)
**Why.** `GH-IN-0001` was found by argument, not by a check: `audit-core` noticed that
§9.4 rested on a completeness property no archive can deliver. Nothing in v0.7 stops
the next engine catalogued as an evidence source from reintroducing the same gap
silently, because the obligation §9.6 creates is not on the conformance list.
**Add to §11's "Mechanically checkable" list:**
> - every repository catalogued in §4 as a source of evidence declares its **emission
> guarantee** in its machine-readable layer declaration: for a **load-bearing**
> source, a local transactional outbox (§9.4) plus a detection surface — heartbeat
> or reconciliation, per §9.6 — and its declared cadence; for an **attributive**
> source, the trade it makes and an explicit statement that completeness is not
> claimed.
**Add after the list:**
> A source that declares nothing is not conforming, and neither is one that declares
> load-bearing emission with rate monitoring alone where its event class is rare
> (§9.6). The declaration is what makes §9.6 checkable rather than reviewable; without
> it the section states an obligation whose satisfaction cannot be observed, which is
> the §9.1 defect this standard has now corrected four times.
>
> Drafted in `gate-house/docs/contracts/approval-emission-detection.md`, which
> `approval-engine`'s `cadence.yaml` implements as the reference instance. Raised by
> `audit-core` as the general form of `GH-IN-0001`, so that the finding that produced
> `GH-WP-0002` cannot recur unnoticed.
---
## A3 — §13 and §13.1, register disposition (T03)
Settled by `GH-DEC-2026-006`. The tables **stay in v0.8** and the four outstanding
stance maps are transcribed into §13.1. The migration to `maturity-engine` is
conditioned on a published export and is therefore a v0.9-or-later change, not this
one.
**Add to §13.1, after the table:**
> This register moves to `maturity-engine` when — and not before — that engine
> publishes a committed, versioned export of §13 and §13.1 that is readable without a
> live query, generated rather than hand-edited, and regenerable so drift between the
> export and the engine's computed state is detectable. A standard of record must stay
> legible in git, at a version, to a reader with no cluster access, including one
> auditing the estate precisely because they do not trust its running systems. A
> pointer to a live engine is an instruction to run software, not a register.
>
> `maturity-engine` already holds both registers as queryable data (`MAT-WP-0001`);
> what remains is publication, which is the migration's precondition rather than its
> follow-up. Settled in `GH-DEC-2026-006`.
**§13.1 rows to transcribe at the cut** — carried here so the cut is mechanical:
| Consumer | Stance map | Axis | Shape |
| --- | --- | --- | --- |
| `ops-warden` | `ops-warden/pep-stance.yaml` | security zone | total per zone; `fail_open` for `z0``z2` and unknown, sanctioned by §9.3 and recorded per §6.4 obligation 1 limb two; test asserts published map equals shipped default |
| `user-engine` | `user-engine/pep-stance.yaml` | security zone | total per zone; `fail_closed` for `z0``z3`, unknown, and not-applicable; test asserts published map equals `user_engine.pep_stance` |
| `tenant-engine` | `tenant-engine/pep-stance.yaml` | security zone | total; `fail_closed` for unset, unreachable, non-allow, unknown; test asserts file equals shipped behaviour |
| `secrets-engine` | `secrets-engine/pep-stance.yaml` | catalog stage — interim, pending zone membership as a claim | total over stage plus unknown; no implicit default; runtime-read; pinned to `SHIPPED_STANCE` by test |
| `ops-mason` | — | — | **unpublished**; PEP-shaped under §6.4 (opening a route). Recorded as a gap, not a stance |
Three of four are total and fail-closed. The `ops-mason` row is the finding §6.4's
closing paragraph anticipated, and it is more useful in the register than absent from
it.
---
## A4 — §9.5, the posture/maturity boundary (T04)
Settled by `GH-DEC-2026-007`, adopting `kings-guard`'s `KG-DEC-2026-002` with one
added clause.
**Append to §9.5, before the approvals/maturity closing paragraph:**
> **The boundary with posture is recomputability, not volatility.** Given the same
> criteria and the same evidence, recompute. If you MUST get the same answer, it is
> maturity and it belongs in an engine. If you CANNOT promise the same answer, it is
> posture and it belongs in Staff.
>
> This is not a new rule — it is the determinism clause above, pointed at the one
> boundary where it had not been pointed. This section already states the maturity
> half: a criterion that cannot be evaluated by rule is not yet a criterion. The
> posture half is its mirror and completes the pair: **a judgment that CAN be
> evaluated by rule is not posture — it is a criterion sitting in the wrong
> repository.** Together they partition rather than describe, which a "fast-moving
> versus slow-moving" line cannot: volatility is an observation about how a value has
> behaved, and every case it does not obviously cover becomes an argument at exactly
> the boundary §6 exists to keep out of argument.
>
> **A criterion MUST bottom out in evidence about the subject, not in another party's
> conclusion about the subject.** A recorded judgment may be evidence *that the
> judgment was made* — a fact with an issuer and a timestamp. It MUST NOT be evidence
> *that the thing judged is so*. Without this, the test is satisfiable by the
> inference it exists to exclude: *"level 2 iff the reviewer marked the control
> adequate"* recomputes identically every time and has placed an opinion inside an
> engine wearing a rule's clothes. For human-attested controls, grade on the
> attestation event's existence, freshness, and issuer — never on its verdict.
>
> Three consequences follow. **The migration direction is permanent**: anything called
> posture that proves recomputable becomes a criterion in `maturity-engine`; anything
> in `maturity-engine` that needs judgment is not yet a criterion and returns to
> Staff. **Two authorities cannot grade the same subject property**, because a
> property is either recomputable or it is not, and that does not depend on which
> repository claims it. **Capability readiness MUST NOT be an input to posture** —
> readiness is deterministic and posture is not, so feeding one into the other would
> make posture partly recomputable and blur the boundary from the Staff side.
>
> **A limit, stated rather than implied.** *"The same evidence"* is not yet well
> defined in this estate. Until §17's request-claim and gap-record schemas exist, two
> parties can disagree about whether they hold the same evidence, and recomputability
> is a thought experiment rather than a check. This makes §17 load-bearing for this
> rule. A boundary that is correct but not yet mechanically checkable is better than
> one that is checkable and wrong; a reader is entitled to know which they are
> holding.
>
> Raised by `kings-guard` against the volatility line this repository had proposed,
> and against its own convenience — the readiness constraint is one it volunteered.
> Settled in `GH-DEC-2026-007`.
---
## A5 — §6.4 and §8, validation by owning layer (T05)
**Why it belongs in the statute.** `GH-DEC-2026-005` settled it on the approval path,
but the property is general: it is a fact about the layer model, not about approvals,
and it currently lives only in a decision record and a contract. The next consumer to
join two artifacts will re-derive it or get it wrong.
**Add as §6.4 obligation 5:**
> 5. **Validation by owning layer.** Where a PEP's decision to act rests on more than
> one artifact, each artifact MUST be validated against the layer that owns its
> data, and a PIP MUST NOT republish the PDP's decision. A PEP MUST NOT accept the
> approval fact from the decision artifact, nor the decision from the approval
> artifact.
>
> A composed object bundling both is not forbidden as an artifact, but it is
> post-decision by construction: it cannot be served from a pre-decision call, and
> it needs a named issuer and lifecycle owner before anyone may rely on it.
>
> The live instance is the `GH-DEC-2026-003` consumption path: the approval-claim
> carries the approval fact (binding digest, validity window, consumption state,
> freshness, issuer) and the `DecisionEnvelope` carries the decision (exact
> `CheckRequest` match, policy package and version pin). Two digests may cover the
> same proposed action without being compared to each other; they answer different
> questions at different layers, and collapsing them is a layer violation in the
> shape of a refactor.
>
> **A consumer of a summary predicate trusts the issuer's evaluation of everything
> folded into it.** Where the split reduces what a PEP verifies independently — as
> `valid_now` does for an approver threshold the claim deliberately does not expose
> — the compensating property is reconstructability at the issuer under §9.6, not a
> second check at the consumer. That is detection, not prevention, and it belongs
> in the same register as §9.6's other residual.
>
> **Correspondence is established by identity, not by translation.** Where two
> artifacts use different vocabularies for the same request, the consumer compares
> a digest one layer computed and the other recorded — it MUST NOT recompute one
> layer's binding from the other's vocabulary, and no cross-engine mapping is
> published for it to use. A mapping can be wrong in a way that still produces a
> confident answer, and it would be a third authority on what a request is.
>
> Raised by `approval-engine`, and by `access-engine` twice against its own
> interest — once on its proposed artifact and once on a gap it declined to close
> locally. Settled in `GH-DEC-2026-005` and `GH-DEC-2026-008`.
**Add to §8's demarcations:**
> **Validating a fact** is not **re-issuing it.** A repository that checks an artifact
> against its issuer is consuming; a repository that serves another layer's conclusion
> as its own output is deciding early (§6.1), whatever the field is named.
---
## A6 — §17, ownership is now assigned (T07)
**Defect.** §17 closes with *"Ownership is proposed, not assigned… Neither has
assented."* That was true at the v0.7 cut and is now false. A reader of the accepted
statute is told the estate has not decided something it has.
**Replace that closing paragraph with:**
> **Ownership of the emission-cadence declaration is assigned.** `info-tech-canon`
> owns the versioned, ecosystem-wide `EmissionCadenceDeclaration` semantic contract —
> generic forms, fields, vocabulary, validation semantics, compatibility, and
> evolution. `net-kingdom` imports it and owns the NetKingdom security profile: which
> evidence classes MUST or SHOULD declare cadence, the prohibition on rate monitoring
> for rare load-bearing classes, and the heartbeat-plus-reconciliation obligations
> that satisfy §9.6. Each source repository owns its declaration instance and its
> emission behaviour; `kings-guard` owns stream evaluation and silence findings, not
> the schema.
>
> The split was made by artifact so that each has one owner: joint ownership would
> leave version authority ambiguous, and giving the whole artifact to either
> repository would conflate a reusable evidence contract with the security
> obligations of one estate. Settled in `GH-DEC-2026-004`; both repositories accepted
> in their own voice (`ITC-WP-0018`, publishing `ITC-EMISSION-CADENCE 0.1` in canon
> `0.7.0`; `NK-WP-0035`, publishing `emission-cadence-security-profile_v0.1.md`). The
> `kings-guard` draft is frozen as assimilation provenance.
>
> **Ownership of the remaining three artifacts is still proposed.** The request-claim
> and gap-record schemas sit between `info-tech-canon` and `net-kingdom` on the same
> unsettled line; the decision-record schema is `access-engine`'s, per above. §2 keeps
> ownership in the owning repository's `INTENT.md`.
---
## A7 — §11 and §12, derived artifacts (T08)
**Why.** Six instances in one week, in four repositories, each caught only because
another repository read the authoritative body:
1. `secrets-engine` built a validator against a `flex-auth` fixture rather than its
prose, producing the `authority == "state-hub"` constant that
`GH-DEC-2026-005` struck — it would have failed closed against every correctly
issued claim.
2. `approval-engine` sourced a revisit trigger from a 2026-08-29 alignment table
rather than the current schema, and Gate House recorded the spent G3 trigger
verbatim.
3. Gate House read a change log for what §3.4 contained — the defect `kings-guard`
raised against v0.6, and the reason
`docs/conformance/2026-09-06-v06-findings-audit.md` checked the body instead.
4. `access-engine` published a dual-control rule written against an assumed claim
shape rather than `schemas/approval_claim.schema.json`: it required
`context.approval.status == "approved"` and counted
`context.approval.approvals[].subject_id`, neither of which exists. Every live
destroy would have denied `dual_control_required`.
5. The inner decision in that same fixture declared `flex-auth.decision-record.v1`
while omitting three provenance fields published since 2026-09-02.
6. `approval-engine`'s own published claim examples omitted `pdp_digest` and
contradicted its own schema — found while implementing `GH-DEC-2026-008`, by the
repository that had been making this point.
**The shape is identical every time**: an authoritative body with a derived artifact
beside it — summary table, example fixture, change log, review record — where the
derivative carries no staleness marker and no mechanical tie to the body. Implementers
read the derivative because it is shorter and concrete.
**Nobody in this list was careless.** Four of the six were reported by the repository
that made the error. Instance 4 was a good-faith rule written against a good-faith
model of another engine's schema; instance 6 was committed by the repository arguing
for the check. The publishing shape makes the error the default, which is what makes
it a conformance item rather than five more corrections. A control that depends on
repositories volunteering corrections is not a control.
### The mechanical half — §11
Where the body is a schema and the derivative is an example, this is checkable and
nearly free. `approval-engine` reports fifteen lines
(`approval-engine/tests/test_examples.py`) validating every published example against
its published schema, and asserting that examples cover **both** shapes of an
optional-but-significant field rather than letting one be inferred from the other. It
would have caught instances 1, 5 and 6 outright, and 4 had the policy package's
fixtures been checked against the claim schema they consume.
**Add to §11's "Mechanically checkable" list:**
> - every published example, fixture, or sample document in an estate repository
> validates against the schema it exemplifies, and where a field is optional but
> load-bearing, the examples cover both its presence and its absence rather than
> leaving one shape to be inferred from the other.
The second clause is the non-obvious one and it is instance 6: an example set that
silently omits an optional field teaches every reader that the field does not exist.
### The convention half — §12
A test cannot tell a reader that a 2026-08-29 table is no longer current. That half is
doctrine, which is why `approval-engine` raised it here rather than solving it.
**Add to §12:**
> **A derived artifact is not evidence of what the body says.** A repository acting on
> another repository's contract, schema, or standard MUST read the authoritative
> artifact. Where a derivative exists — a summary, example, change log, or review
> record — it MUST be marked as derived, name the artifact it derives from, and carry
> the version or commit it was derived at. A **dated review record** MUST carry an
> explicit marker that it states status as of that date and not current state.
> Where a derivative is unmarked, treat it as stale.
>
> This is not a counsel of care. The estate produced six instances in a single week
> across four repositories; four were self-reported, and one was committed by the
> repository arguing for this rule. Diligence prevented none of them.
**Note on scope.** The mechanical half catches example-versus-schema drift. The
convention half makes prose staleness visible but does not detect it: a marked
derivative can still be wrong, and marking does not measure how wrong. Naming that
limit here so the check is not later read as stronger than it is — which would be this
same defect one layer up.
Proposed by `approval-engine`, which asked to own none of it.
---
## A8 — §6.4 and §13.1, `unknown` and the scoping axis (T09)
Settled by `GH-DEC-2026-009`, on the first occasion §13.1 held enough rows to diverge.
**Add to §6.4 obligation 3:**
> **`unknown` is not a zone and MUST resolve to `fail_closed`.** §9.3 permits trading
> availability for openness *per zone* — knowingly, for a named scope, at a declared
> cost. That trade requires knowing the zone. Where the scope is unknown it cannot
> have been made for this request, so resolving `unknown` permissively does not extend
> a considered decision, it invents the most permissive one available.
>
> An unreachable engine and an unclassified subject are different failure cases. The
> first is a known request in a degraded system and the trade is available for it. The
> second is not: `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 — and a permissive `unknown` makes being
> unclassifiable a privilege escalation requiring no credential. §8's asymmetry
> forbids that wherever it appears.
>
> Zone stances are untouched by this. Raised by `access-engine` as an observation on
> two conformant maps that disagree, and explicitly not as a request that either
> consumer change its stance — a PDP does not set a consumer's stance.
**Add to §6.4 obligation 3, and to the §13.1 register columns:**
> A published map MUST name the **axis** it scopes over and state its relationship to
> security zone: either a mapping, or an explicit declaration that none exists yet and
> why. §13.1 carries the axis per row.
>
> "Per zone **or equivalent scope**" permits axes that cannot be aggregated, and the
> register cannot then answer *"what is the estate's stance for a `z2`-protected
> workload"* — the question an inventory exists to answer. Forcing every consumer onto
> zones is the wrong repair where zone membership is not yet available as a claim;
> asserting a zone one cannot know is a fiction, and a fiction in a runtime-read,
> test-pinned file is worse than an honest incommensurability. **The register therefore
> records the axes and states that cross-axis aggregation is unavailable**, rather than
> implying an answer it does not have. An inventory that silently cannot answer its own
> question is §9.1's defect in another place.
>
> The convergence path is zone membership reaching the decision as a claim, after
> which a stage-scoped map converges onto zones without either side inventing a
> mapping. Until then the axes are recorded, not reconciled.
**Register impact at the cut.** `ops-warden`'s `unknown``fail_open` cell becomes
non-conformant under this amendment; its `z0``z2` stances do not. That is a real cost
to the repository that published first, built the reference form, and offered it
estate-wide, and it is put to the assent round rather than imposed — the A3 row stays
as written until v0.8 is accepted.
---
## Not in this set
Recorded so the omissions are deliberate rather than forgotten.
- **§12's fourth step remains unstaffed.** Observation in production is still not
live; `kings-guard`'s `qonto-assistant` pilot is fixture-only. That is a status, not
an amendment, and §12 already says so.
- **§16's open questions are not re-cut here.** Several have been answered by decision
records since v0.7. Reconciling §16 against the decision log is `T06` work at
assembly, not a separate normative amendment.
- **No §4 catalog change.** None of these amendments moves a capability between
repositories.