Review security layer model v0.6 — assent, three findings
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

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 <noreply@anthropic.com>
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
This commit is contained in:
tegwick 2026-08-29 10:19:05 +02:00
parent c7a0ce9557
commit cecc3659b9
2 changed files with 195 additions and 0 deletions

View file

@ -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.

46
layer.yaml Normal file
View file

@ -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]