Review layer model v0.4; correct an unsound audit claim it exposes
Assessment in history/2026-08-29-layer-model-v04-review.md. No objection to the ruling; both ops-warden amendments were adopted (5.2 conduit, 5.3 declared gap). Three findings, one against us. The one against us is real. 9.6 requires emission atomic with the state change for load-bearing evidence. ops-warden ca.py carries `pass # audit must not block signing` and AuditTrail.md advertises that the trail never blocks the primary action, so a failed append loses the event while the cert still issues -- a suppressed event leaving the chain intact, which is exactly what 9.6 describes. Whether to make it atomic is gate-house doctrine, not ops-wardens call: it would give the estates operational access lane a new dependency on its own evidence store. But one half of the fix is ours regardless -- the trail must not be read as complete. AuditTrail.md now says absence of a record is not evidence of absence, which it did not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ Assistant: claude-code Assistant-Model: opus Assistant-Process: 4014535@bnt-lap001 Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
This commit is contained in:
parent
f815bb35ca
commit
ec625873fb
2 changed files with 145 additions and 0 deletions
136
history/2026-08-29-layer-model-v04-review.md
Normal file
136
history/2026-08-29-layer-model-v04-review.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Security Layer Model v0.4 — ops-warden's review
|
||||
|
||||
**Date:** 2026-08-29
|
||||
**Reviewed:** `net-kingdom/canon/standards/security-layer-model_v0.4.md` (accepted)
|
||||
**Prior position:** `ADR-0010`, assent to v0.1 (`WARDEN-IN-0001`)
|
||||
**Outcome:** no objection to the ruling; three findings, one of them against ops-warden.
|
||||
|
||||
---
|
||||
|
||||
## What v0.4 did with ops-warden's amendment
|
||||
|
||||
Both §5 asks from `ADR-0010` were adopted.
|
||||
|
||||
**§5.3 declared engine gap** is the amendment ops-warden offered, adopted with the
|
||||
four fields intact (`capability`, `intended_owner`, `blocked_on`, `review`), the
|
||||
rationale preserved — *a rule offering no lane for a real sanctioned case gets
|
||||
satisfied by relabelling rather than by closing the gap* — and the framing that
|
||||
matters most kept explicit: **a declared gap is tracked non-conformance, not
|
||||
conformance**. ops-warden's delegation machinery is cited as prior art.
|
||||
|
||||
**§5.2 conduit** resolves the question ops-warden flagged rather than assumed. The
|
||||
test is the supplied-authority property, which is the right test: it turns on what
|
||||
the repository presents, not on what it touches. *"A conduit that presents its own
|
||||
token is not a conduit"* is a sharper statement of `ADR-0002` than `ADR-0002` makes.
|
||||
|
||||
**This created an obligation ops-warden had not met.** §5.3 requires the fields
|
||||
*machine-readably* and §11 makes the mapping a mechanical check; ops-warden's
|
||||
declaration was prose in `INTENT.md`. Fixed in this pass: `layer.yaml`,
|
||||
`scripts/check_layer_conformance.py`, `tests/test_layer_conformance.py`. The
|
||||
checker found three undeclared modules on first run, all false positives — help
|
||||
text, a docstring, and the doubles library that *simulates* `bao` — which is why
|
||||
it now matches invocation shapes rather than the word.
|
||||
|
||||
---
|
||||
|
||||
## Finding 1 — §9.1 and §5.3 disagree, and ops-warden's §4 row is the instance
|
||||
|
||||
§9.1: *a Staff repository MUST NOT be catalogued in §4 as owning a capability that
|
||||
requires a Tooling contact no engine exposes*; where intended but unbuilt, the
|
||||
entry **MUST be marked pending** and the gap declared under §5.3.
|
||||
|
||||
ops-warden's §4 row reads `operational access lanes, stewardship, runbooks; SSH
|
||||
certificate issuance` — with no pending mark. And §13 lists *SSH-CA signing write
|
||||
(`VaultCA`, `bao kv put`) — declared by ops-warden — intended owner secrets-engine*.
|
||||
|
||||
So the catalog asserts ownership of a capability that requires a Tooling contact no
|
||||
engine exposes, unmarked. By §9.1's own text that is a defect. But the available
|
||||
fix is worse than the defect: **marking it pending would be false.** SSH issuance is
|
||||
production-verified and in daily use. `pending` would tell a reader ops-warden does
|
||||
not yet do the one thing it demonstrably does.
|
||||
|
||||
The root cause is that §9.1 collapses two different states:
|
||||
|
||||
| State | Example | Capability today |
|
||||
| --- | --- | --- |
|
||||
| No route exists at all | kings-guard containment (§9.2) | **zero** |
|
||||
| Route exists via a declared §5.3 gap | ops-warden SSH issuance | **working, tracked** |
|
||||
|
||||
§5.3 exists precisely to sanction the second. §9.1 was written for the first — it
|
||||
was raised by kings-guard, about containment, and correctly fixed *for that case*.
|
||||
Applied to the adjacent case it produces a false catalog.
|
||||
|
||||
**Recommendation:** give §9.1 two marks rather than one — `pending` where no route
|
||||
exists, and `declared-gap` where the capability is discharged under §5.3 and
|
||||
registered in §13. Both are honest; today's binary forces a choice between a false
|
||||
label and an unmarked violation.
|
||||
|
||||
This is the §12 loop working as designed, and §12 already says so: a finding that a
|
||||
rule is unsatisfiable is a success of the loop.
|
||||
|
||||
---
|
||||
|
||||
## Finding 2 — §5's scope is undefined for infrastructure §4 does not catalogue
|
||||
|
||||
§5 forbids *a direct client for a Tooling-layer system*. §4 catalogues the security
|
||||
estate, and only `key-cape` and `OpenBao` are Tooling rows.
|
||||
|
||||
ops-warden holds an HTTP client for the **State Hub** and for **llm-connect**
|
||||
(`src/warden/worker.py`). Neither appears in §4. Both are infrastructure a Staff
|
||||
repository holds a direct client for.
|
||||
|
||||
The question is not rhetorical, because the answers diverge sharply:
|
||||
|
||||
- **If they are Tooling**, then every Staff repository in the estate is in
|
||||
undeclared violation on adoption day — they all write progress events — and
|
||||
§11's second mechanical check fails estate-wide.
|
||||
- **If they are not**, §5 should say so, because *"a Tooling-layer system"* reads
|
||||
considerably broader than *"a repository in the §4 Tooling rows"*.
|
||||
|
||||
ops-warden has recorded both under `non_tooling_clients` in `layer.yaml` with the
|
||||
reasoning stated, rather than resolving it unilaterally. The scope is gate-house's
|
||||
to set.
|
||||
|
||||
---
|
||||
|
||||
## Finding 3 — §9.6 lands on ops-warden, and ops-warden does not satisfy it
|
||||
|
||||
This is the one against us, and it is the most consequential item in the review.
|
||||
|
||||
§9.6 consequence 1: *any system whose evidence is load-bearing MUST make emission
|
||||
atomic with the state change it records. An archive cannot retrofit completeness.*
|
||||
|
||||
**ops-warden's audit emission is deliberately non-atomic.** `src/warden/ca.py:90`
|
||||
carries `pass # audit must not block signing`, and `wiki/AuditTrail.md` states the
|
||||
trail *"never blocks the primary action"*. If the audit append fails, the
|
||||
certificate is still issued and the event is simply lost — a suppressed event that
|
||||
leaves the chain perfectly intact, which is the exact failure §9.6 describes.
|
||||
|
||||
That was a considered availability choice: an audit-disk problem should not remove
|
||||
production host access. §9.6 now makes it a conformance question, and the trade is
|
||||
real in both directions:
|
||||
|
||||
- make emission atomic → an audit write failure fails the sign, and the estate's
|
||||
operational access lane acquires a new dependency on its own evidence store;
|
||||
- leave it → signing evidence cannot be treated as complete, and anything reasoning
|
||||
from *"there is no record of a sign"* is unsound.
|
||||
|
||||
**ops-warden has not changed it, and is not going to decide this alone** — §9.6 is
|
||||
estate doctrine and the question is whether SSH signing evidence is load-bearing in
|
||||
gate-house's sense. What ops-warden can say is that the second horn is currently
|
||||
true and undocumented: `wiki/AuditTrail.md` does not warn that absence of a record
|
||||
is not evidence of absence. That correction is ops-warden's regardless of the
|
||||
ruling, and is the smaller half of the fix.
|
||||
|
||||
Note also that §5.2 requires a conduit action to be *"reconstructable as the
|
||||
caller's action in audit"* — an audit-dependent claim, and therefore bounded by
|
||||
§9.6. Worth a cross-reference so the two rules do not drift apart.
|
||||
|
||||
---
|
||||
|
||||
## Offered
|
||||
|
||||
`layer.yaml` + `check_layer_conformance.py` + `test_layer_conformance.py` is a
|
||||
working reference implementation of §5.3 and of §11's second mechanical check. Eight
|
||||
of fifteen estate repositories have yet to declare (§14). If it is useful as a
|
||||
pattern to point them at, it is offered — as the delegation machinery was.
|
||||
|
|
@ -32,6 +32,15 @@ fresh file starts.
|
|||
high-entropy runs). Signing and proxy paths swallow audit failures so gatekeeping
|
||||
never blocks the primary action — but tests prove values cannot be written.
|
||||
|
||||
> **Absence of a record is not evidence of absence.** Because emission never
|
||||
> blocks the primary action (`src/warden/ca.py`), a failed append loses the event
|
||||
> while the action still happens. This trail proves that the records it holds were
|
||||
> not altered or truncated; it does **not** prove that every action produced one.
|
||||
> Do not reason from a missing entry. This is the estate-wide bound in
|
||||
> `security-layer-model_v0.4` §9.6 — completeness is the source's obligation, and
|
||||
> whether ops-warden must make signing emission atomic is an open question with
|
||||
> gate-house (`history/2026-08-29-layer-model-v04-review.md`, Finding 3).
|
||||
|
||||
---
|
||||
|
||||
## Query
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue