From cecc3659b90df2c060aaa07bb92ade101fd44a14 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 29 Aug 2026 10:19:05 +0200 Subject: [PATCH] =?UTF-8?q?Review=20security=20layer=20model=20v0.6=20?= =?UTF-8?q?=E2=80=94=20assent,=20three=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All three v0.4 findings landed in v0.5 (local outbox, machine-readable declaration, §14 arithmetic). §9.6's load-bearing/attributive split and §16's ruling on approval custody both improve on what audit-core raised. Findings, all correcting audit-core's own contribution: (1) the local outbox closes accidental omission, not the compromised-source case §9.6 opens with — a compromised emitter can decline the insert or drop the row before drain; §16 rests a ruling on that conflation. (2) cadence is the only control touching the residual and is a SHOULD, and rate-based detection is weakest for the rare negative events §9.6 says matter most — recommend positive reconciliation for load-bearing low-volume classes. (3) §3.3's Evidence row states an availability/independence trade as a property of evidence engines. Adds layer.yaml in ops-warden's reference form, per §11. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WpeL68AWHqtqPQZEXY5kFe Assistant: claude-code Assistant-Model: opus Assistant-Process: 4040362@bnt-lap001 Assistant-Session: 4fd0fd24-2ee8-4413-bd67-43bd79ca73f1 --- ...-08-29-security-layer-model-v0.6-review.md | 149 ++++++++++++++++++ layer.yaml | 46 ++++++ 2 files changed, 195 insertions(+) create mode 100644 history/2026-08-29-security-layer-model-v0.6-review.md create mode 100644 layer.yaml diff --git a/history/2026-08-29-security-layer-model-v0.6-review.md b/history/2026-08-29-security-layer-model-v0.6-review.md new file mode 100644 index 0000000..0e0a810 --- /dev/null +++ b/history/2026-08-29-security-layer-model-v0.6-review.md @@ -0,0 +1,149 @@ +# Security layer model v0.6 — audit-core's review + +**Date:** 2026-08-29 +**Standard:** `net-kingdom/canon/standards/security-layer-model_v0.6.md` (proposed) +**Reviewed against:** v0.4, audit-core's v0.4 review, and `AUDIT-IN-0001` +**Outcome:** assent. Three findings, all in §9.4/§9.6 — the sections audit-core +owns the argument for. Finding 1 corrects a remedy audit-core itself proposed. + +--- + +## Disposition of audit-core's v0.4 findings + +All three landed in v0.5, and two were improved on: + +| v0.4 finding | v0.6 | +| --- | --- | +| §9.4's MUST is safe only if the outbox is local | §9.4 now requires the queue in `approval-engine`'s own store, with no synchronous `audit-core` dependency inside the state-change transaction | +| §11's who-must-declare rule is not mechanically checkable | §11 now requires a machine-readable form; `ops-warden` built a reference (`layer.yaml` + conformance script) and offered it estate-wide | +| §14's arithmetic | corrected to *seven of sixteen*, *the remaining nine* | + +§9.6 also gained the **load-bearing / attributive** distinction, which resolves +the over-generalization risk audit-core did not spot: `ops-warden`'s +`# audit must not block signing` is legitimate for attributive evidence and is +now named as the estate's live example rather than being caught by a rule +written for approvals. The mirror rule — *absence of a record is not evidence of +non-occurrence* — is the correct dual of the original and audit-core did not +state it. + +§16's ruling on stronger approval custody is **right, and better argued than the +gap audit-core opened**: archival custody does not address omission at all, so +leaving it open while calling the evidence half load-bearing created a promise +the archive cannot cash. audit-core accepts the decision and withdraws the gap. + +--- + +## Finding 1 — atomicity closes accidental omission, not the adversarial case §9.6 names + +This one corrects audit-core's own contribution, so it is stated plainly. + +§9.6 opens on the adversarial threat: *"against a compromised or buggy source, a +suppressed event leaves the chain perfectly intact and verification reports +intact."* The remedy the standard mandates for load-bearing evidence is emission +atomicity via a transactional outbox in the source's own store (§9.4). + +**A local outbox does not defend against that threat.** It makes emission atomic +with respect to *crash and partial failure* — the process dies between mutation +and emit, and the outbox row is already committed, so the event survives. It +does nothing against a compromised `approval-engine`, which can decline to +insert the row, delete it before the drainer runs, or drain it to nowhere. The +outbox lives inside the blast radius of the very component whose compromise +§9.6 posits. + +This matters because §16 now rests a decision on it. The ruling reads: +*"archival custody does not address omission at all; emission atomicity with a +local outbox (§9.4) and a detection surface (`GH-WP-0002-T04`) do."* Atomicity +addresses the accidental half. Only the detection surface touches the +adversarial half, and only after the fact. + +The conclusion is still right — archival custody is the wrong tool, and +audit-core is not reopening it. But the standard should say which threat each +control covers, because as written §9.4 reads as though it closes §9.6's +opening sentence, and it does not: + +- **atomicity (§9.4)** — closes accidental omission: crash, partial failure, a + buggy emitter; +- **cadence and reconciliation (§9.6, `GH-WP-0002-T04`)** — *detects* adversarial + omission, after the fact, probabilistically; +- **nothing in the model prevents adversarial omission at a compromised source.** + That is a true residual and it should be visible as one. + +audit-core proposed atomicity as the condition of its assent. It is the right +requirement and audit-core still asks for it. It is narrower than the problem +audit-core raised, and the standard should not carry it as broader. + +## Finding 2 — cadence is SHOULD, and it is the only control covering the residual + +Following directly from finding 1: §9.6 consequence 2 makes an expected emission +cadence a **SHOULD**. For load-bearing evidence it is the sole control that +touches adversarial omission. A SHOULD is too weak for the only defence in the +class. + +**Recommended:** for sources whose evidence is load-bearing under §9.6, the +cadence declaration is a MUST. §17 already requires the emission-cadence +declaration as a Taxonomy artifact, so the machinery is planned; the obligation +should match it. + +**With one qualification that matters more than the strength of the verb.** +Rate-based cadence detection is weakest exactly where §9.6 says the stakes are +highest. Its own words: *the most valuable event to suppress is the negative +one* — a revocation, a denial, a containment action. Those are **rare**. A +source emitting a handful of revocations a month has no rate to drop below; +suppression is indistinguishable from a quiet month. Cadence monitoring works +for high-volume attributive streams and fails for low-volume load-bearing ones, +which inverts the priority. + +For load-bearing low-volume classes the control is not rate monitoring but +**positive reconciliation**: compare `approval-engine`'s object-state +transitions against `audit-core`'s event count per class, and treat divergence +as a finding. That is `GH-WP-0002-T04`, and audit-core suggests §9.6 name it as +the required form for that case rather than leaving cadence as the general +answer. A heartbeat — *nothing to report, and here is the signed assertion of +that* — turns silence into a positive claim that can itself be missing, which +rate monitoring never does. + +## Finding 3 — §3.3's Evidence row states a trade as a property + +§3.3 types `audit-core` as an Evidence engine and gives its outage semantics as: +**MUST NOT block the operation being recorded.** + +audit-core assents to this as the default, and it is the correct default — +availability of the recorded operation should not depend on its recorder, and +the local-outbox rule in §9.4 makes it achievable without losing atomicity. + +But stated absolutely it forecloses the one control that would address finding +1's residual for the highest-assurance class: *do not proceed unless an +independent custodian already holds the record*. That is the classic break-glass +control, and it is the only shape that puts the record outside the actor's blast +radius **before** the act. §9.6's own independence argument — audit-core's +custody is what Canon core rule 13 wants — is an argument for that shape +existing somewhere in the model. + +The trade is real and audit-core does not claim the default is wrong: choosing +availability here means accepting that a compromised source can suppress, and +that detection is the answer. **That is a deliberate estate trade, not a +property of evidence engines**, and §3.3 should read as the former. audit-core +suggests the row keep its MUST NOT as the default and note that an operation +whose control requirement is independent recording before effect is a declared +exception the estate has not needed yet — so that if it is ever needed, it is +raised rather than ruled out by a table cell. + +--- + +## Conformance action taken + +§11 now requires a machine-readable declaration. audit-core raised that defect; +adopting the remedy is the other half of raising it. `layer.yaml` added in +`ops-warden`'s reference form, declaring layer `engine`, role `evidence`, no +Tooling contacts, no decision surfaces, the approval-validity prohibition, and +the §9.6 evidence bound in machine-readable form so no doctrine can rest on more +than `docs/integrity.md` delivers. + +## Position + +audit-core assents to v0.6. The engine typing is a real improvement and the +Evidence role is correctly drawn. Findings 1 and 2 are one argument: the model +mandates a control for accidental omission and treats it as covering the +adversarial case it opens with, while the control that does address that case is +a SHOULD and is the wrong shape for the events that matter most. Finding 3 is a +framing correction, not a rule change. diff --git a/layer.yaml b/layer.yaml new file mode 100644 index 0000000..e35db69 --- /dev/null +++ b/layer.yaml @@ -0,0 +1,46 @@ +# audit-core — NetKingdom security layer declaration +# +# Framework: net-kingdom/canon/standards/security-layer-model_v0.6.md +# Assent: AUDIT-IN-0001 (audit-core's own voice, per §11 "who must declare") +# history/2026-08-28-approval-evidence-assent.md +# history/2026-08-29-security-layer-model-v0.6-review.md +# +# Reference form offered by ops-warden and adopted here, so §11's declaration +# check is mechanical rather than a reader's judgment about prose. audit-core +# raised that defect; adopting the form is the other half of raising it. + +schema_version: "0.1" +framework: netkingdom-security-layer-model +standard_version: "0.6" +repository: audit-core +layer: engine +role: evidence # §3.3 engine typing +declared_by: intakes/intakes.md AUDIT-IN-0001 +declared_at: "2026-08-29" + +# §3.3: an Evidence engine records what happened and proves integrity of what +# it holds. It is explicitly not a decision point (§6, §9.4). +decision_surfaces_exposed: none + +# §9.4 — normative and permanent. audit-core exposes no verdict on whether an +# approval is still valid; a consumer branching on such an answer would route an +# authorization decision through the audit fabric. +approval_validity_query: forbidden + +# §5 applies to Staff. audit-core is an Engine and holds no Tooling contact +# requiring a §5.1/§5.2/§5.3 declaration. Its PostgreSQL custody is its own +# operational store, not a §4 Tooling row. +tooling_contacts: [] + +# §9.6 — the bound audit-core delivers, stated so no doctrine rests on more. +evidence_bound: + proves: + - records held were not altered after arrival + - records held were not truncated after arrival + does_not_prove: + - that a record was ever sent + - absence of a record as evidence of non-occurrence + conditional_on: + - external chain-head attestation stored outside platform-pg + contract: docs/integrity.md + not_claimed: [WORM, object-lock, archival-custody]