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