The standard is accepted at v0.7 and all three of audit-core's v0.6 findings landed in it (§9.6 threat decomposition, cadence MUST for load-bearing sources with reconciliation/heartbeat for low-volume classes, §3.3's Evidence row restated as an estate trade). INTENT.md: layer/role declared in frontmatter as §11 and companion §2 require — layer.yaml alone did not discharge it. Layer section rewritten for the Evidence role and its obligations. New Evidence Bound section carrying the §9.6 sound/unsound forms and the three-row threat table, including the residual nothing in the model prevents. SCOPE.md: replaced the statehub register stub, which carried no boundary at all. Statute-fixed prohibitions now live here, separated from the merely-not-yet — §16 ruled the stronger-custody gap closed, so WORM and data.archive are not ours rather than not yet. Assessment found nine gaps. Headline: postgres_backend returns tamper_evidence=True unconditionally while docs/integrity.md permits it only against a live external attestation, and the one on record is 2026-08-16 with no job renewing it — audit-core overclaiming its own bound, the §9.6 defect turned inward. Also: no cadence, heartbeat, reconciliation, or load-bearing classification exists, so the obligation audit-core argued up from SHOULD to MUST is not yet dischargeable against audit-core. AUDIT-WP-0009 raised, ten tasks. 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
177 lines
8.8 KiB
Markdown
177 lines
8.8 KiB
Markdown
# Security layer model v0.7 — alignment, scope revision, and gap assessment
|
||
|
||
**Date:** 2026-08-29
|
||
**Standard:** `net-kingdom/canon/standards/security-layer-model_v0.7.md` — **accepted**
|
||
**Companion:** `net-kingdom/SECURITY-COMPANION.md` v0.2
|
||
**Scope of this review:** align `INTENT.md`, revise `SCOPE.md`, assess scope
|
||
against intent and against the shipped implementation, and raise the work.
|
||
**Result:** `INTENT.md` and `SCOPE.md` updated; nine gaps found, one of them a
|
||
live overclaim; `AUDIT-WP-0009` raised.
|
||
|
||
---
|
||
|
||
## 1. Disposition of audit-core's v0.6 findings
|
||
|
||
All three landed in v0.7:
|
||
|
||
| v0.6 finding | v0.7 |
|
||
| --- | --- |
|
||
| atomicity closes accidental, not adversarial, omission | §9.6 gained the three-row **threat decomposition**, including the explicit *nothing in this model prevents it* residual |
|
||
| cadence is a SHOULD and is the only control in its class | §9.6 cadence is **MUST for load-bearing sources**, with positive reconciliation or a heartbeat as the required form for low-volume classes |
|
||
| §3.3's Evidence row states a trade as a property | restated as an estate trade, leaving independent-recording-before-effect raisable as a declared exception |
|
||
|
||
The standard is now accepted. What follows is not review of gate-house's text
|
||
but assessment of audit-core against it.
|
||
|
||
## 2. What v0.7 binds audit-core to
|
||
|
||
Audit Core is **Engine / Evidence** (§3.3, §4). Its obligations:
|
||
|
||
1. Declare the layer in `INTENT.md` **frontmatter** plus prose in its own voice
|
||
(§11, companion §2). `layer.yaml` alone does not discharge this.
|
||
2. Render and cache no decision; expose no approval-validity query (§6, §9.4).
|
||
3. Never claim more than the archive delivers, in either direction (§9.6).
|
||
4. **Support** the §9.6 obligations it argued for: the load-bearing/attributive
|
||
distinction, declared emission cadence, and reconciliation or heartbeat for
|
||
low-volume load-bearing classes.
|
||
5. Register `approval-engine` as a distinct source (§9.4, `AUDIT-IN-0001`).
|
||
6. Keep the §5 conformance check total by listing infrastructure contacts even
|
||
where uncatalogued (companion §4), noting that carve-out sunsets within two
|
||
review intervals.
|
||
|
||
Point 4 is the one with teeth. audit-core argued cadence up from SHOULD to MUST
|
||
and specified the reconciliation form. **A source cannot declare a cadence to a
|
||
system with nowhere to put it.** Having won the obligation, audit-core owes the
|
||
surface that makes it dischargeable.
|
||
|
||
## 3. Scope versus intent
|
||
|
||
`SCOPE.md` was a `statehub register` stub — *"audit-core exists to provide the
|
||
capability described in INTENT.md"* — carrying no boundary at all. Against an
|
||
`INTENT.md` that had grown a Layer section, an Approval Evidence section, and a
|
||
corrected principle 6, the gap was total: **every statute-fixed prohibition
|
||
lived only in INTENT and none of it in SCOPE**, which is the file a contributor
|
||
checks before adding a surface.
|
||
|
||
`SCOPE.md` is rewritten to carry the permanent boundaries explicitly — no
|
||
decision, no validity query, no operative approval state, no completeness claim
|
||
— and to separate them from the merely-not-yet. The distinction matters: §16
|
||
ruled the stronger-custody gap **closed**, so WORM and `data.archive` are now
|
||
*not ours* rather than *not yet*, and the old scope line implying a pending
|
||
archive requirement was stale.
|
||
|
||
Intent and scope now agree. The disagreement that remains is between both of
|
||
them and the code.
|
||
|
||
## 4. Gaps
|
||
|
||
### G1 — `tamper_evidence` is claimed unconditionally *(headline)*
|
||
|
||
`audit_core/postgres_backend.py:306` returns `tamper_evidence=True` as a
|
||
constant. `docs/integrity.md` permits that claim **only when**:
|
||
|
||
1. `verify` exists and fails on a rewritten row; **and**
|
||
2. an external head attestation exists, and verify-against-attestation reports a
|
||
missing cited head as a break.
|
||
|
||
Condition 2 is operational, not structural. The only attestation on record is
|
||
`docs/evidence/chain-head-20260816.json` — thirteen days old — and `deploy/`
|
||
contains no job that renews it. Every sender reading `/readyz` is therefore told
|
||
audit-core has a property whose precondition is unverified and probably stale.
|
||
|
||
This is §9.6's defect turned inward: **audit-core claiming more than its bound
|
||
delivers**, in the exact idiom it corrected in gate-house's own doctrine twice.
|
||
It is the highest-priority gap for that reason, independent of severity.
|
||
|
||
Fix: derive the flag from live attestation state, and make a stale or absent
|
||
attestation degrade the claim rather than leave it standing.
|
||
|
||
### G2 — no load-bearing / attributive classification
|
||
|
||
`SenderIdentity` (`audit_core/senders.py:30`) carries `sources`, `tenants`,
|
||
`may_write`, `may_read`, `secret_policy`, `expires_at`. Nothing records which
|
||
kind of evidence a source produces, so audit-core cannot tell which sources owe
|
||
a cadence. §9.6's central distinction is unrepresentable.
|
||
|
||
### G3 — no cadence declaration
|
||
|
||
Nothing accepts, stores, or evaluates a declared emission cadence. `grep` for
|
||
`cadence` across `audit_core/`, `docs/`, and `tests/` returns nothing; the
|
||
`reconcile` hits are duplicate-event reconciliation, an unrelated concept.
|
||
Depends on the §17 emission-cadence schema, which `kings-guard` is drafting.
|
||
|
||
### G4 — no heartbeat, and no missing-heartbeat finding
|
||
|
||
The required form for low-volume load-bearing classes does not exist. This is
|
||
the gap that matters most substantively: it is the only control covering
|
||
adversarial omission for exactly the rare negative events — revocations,
|
||
denials, containment — where suppression is most valuable and least visible.
|
||
|
||
### G5 — no reconciliation surface
|
||
|
||
A source cannot compare its own state transitions against audit-core's event
|
||
count per class, because no endpoint exposes those counts. `GH-WP-0002-T04` is
|
||
the reference instance and has no counterpart here.
|
||
|
||
### G6 — stream-completeness findings have no home
|
||
|
||
`/v1/dead-letters` and `/v1/secret-findings` exist; a stream-observation finding
|
||
has no surface. INTENT principle 10 already says degraded audit streams are
|
||
themselves audit and operations events — the principle is in place and the
|
||
mechanism is not.
|
||
|
||
### G7 — `approval-engine` not registered
|
||
|
||
Committed to in `AUDIT-IN-0001`. Needs sender registration, the four event
|
||
classes, tenancy mapping, retention profile, and `secret_policy`, onboarded
|
||
under principle 4 — declared policy, not merely arriving events. Not blocking:
|
||
`approval-engine` is not yet emitting.
|
||
|
||
### G8 — no negative test on the approval-validity prohibition
|
||
|
||
§9.4's MUST NOT is currently honoured by absence. The estate's own idiom for a
|
||
published-equals-shipped property is a test (§6.4 obligation 3 requires exactly
|
||
that of a PEP stance map). A prohibition worth stating is worth asserting.
|
||
|
||
### G9 — Tooling contact list is empty rather than total
|
||
|
||
`layer.yaml` declares `tooling_contacts: []`, true under §5 as written since
|
||
audit-core is an Engine holding no `key-cape` or OpenBao client. But companion
|
||
§4 asks that uncatalogued infrastructure be listed anyway so the check is total,
|
||
and the carve-out sunsets within two review intervals: `platform-pg` is a store
|
||
another layer reads. Listing it now costs nothing and pre-empts the sunset.
|
||
|
||
## 5. What is already conforming
|
||
|
||
Not everything is a gap, and the assessment would be dishonest without this:
|
||
|
||
- **No decision surface exists.** The §6 prohibition is met in fact.
|
||
- **The integrity contract is honest.** `docs/integrity.md` states the proof
|
||
bound, names the attacker class it does not cover, and explicitly disclaims
|
||
WORM and `data.archive`. G1 is a defect in the *code's* claim, not the
|
||
document's — the doctrine was right and the implementation drifted from it.
|
||
- **Custody claims are otherwise sober**: `custody_class=operational` rather than
|
||
`archive`, recovery cited to the platform window rather than `retention_days`.
|
||
- **Tenant isolation and redaction** are built and tested.
|
||
- **`immutable=True`** is correctly qualified in-code as not a claim against the
|
||
database owner.
|
||
|
||
The pattern is worth naming: where audit-core wrote doctrine it was accurate;
|
||
where a value was hard-coded it drifted optimistic. G1 is the one place the two
|
||
diverge, and it diverges in the direction the estate is least able to detect.
|
||
|
||
## 6. Priority
|
||
|
||
| Gap | Priority | Why |
|
||
| --- | --- | --- |
|
||
| G1 `tamper_evidence` | **high** | live overclaim to every sender; audit-core's own doctrine violated inward |
|
||
| G2 classification | high | prerequisite for G3–G5 |
|
||
| G4 heartbeat | high | only control covering the adversarial residual for rare events |
|
||
| G3 cadence | medium | blocked on the §17 schema |
|
||
| G5 reconciliation | medium | `GH-WP-0002-T04` counterpart |
|
||
| G8 negative test | medium | cheap; closes a MUST NOT by assertion |
|
||
| G6 findings surface | medium | needed for G3/G4 to be actionable |
|
||
| G7 approval-engine source | low | not blocking; no emitter yet |
|
||
| G9 contact list | low | pre-empts a sunset |
|
||
|
||
Raised as `AUDIT-WP-0009-evidence-role-conformance.md`.
|