audit-core/history/2026-08-29-v0.7-alignment-and-scope-assessment.md

178 lines
8.8 KiB
Markdown
Raw Normal View History

Align to Security Layer Model v0.7; revise SCOPE; raise AUDIT-WP-0009 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
2026-08-29 14:42:51 +02:00
# 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 G3G5 |
| 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`.