178 lines
8.8 KiB
Markdown
178 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`.
|