docs: assent to v0.8 obligation 3, and price its adoption
gate-house circulated security-layer-model v0.8, whose §6.4 obligation 3
makes our `unknown: fail_open` cell non-conformant, and asked to be argued
with rather than obeyed.
Finding 1 -- assent. They wrote the falsifier into GH-DEC-2026-009: a scope
genuinely unknown AND genuinely low-consequence, expected to be a §5.1
read-only diagnostic. We looked and do not have one. The stance map governs
`warden sign` -- a credential-issuing side effect -- so §5.1 does not reach
it and the argument stands. Being unclassifiable must not buy permissiveness.
Finding 2 -- adopting it today would be a global fail-closed flag in all but
name. Of four signing targets, zero resolve to a zone and three are
`unknown`, so the cell would fail closed on essentially every certificate
during a flex-auth outage -- including the SSH certificate needed to reach
the host and repair flex-auth. That is exactly ADR-0006's rejected
configuration reached by another route. Asked for a dated transition gated
on coverage, or failing that for §13.1 to record coverage alongside stance:
a row reading `unknown: fail_closed` while every target is unknown is
conformant and misleading.
Not flipping the cell. It is a proposed standard, 18 of our 18 unknown
lanes are unknown because another repo has not declared, and ADR-0009 rule 3
forbids closing that with inference -- a stricter stance is not a licence to
manufacture the membership that makes it survivable. WARDEN-WP-0040 records
the order: classify the continuity path, raise coverage by asking owners,
then supersede ADR-0009's unknown row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EPuTc18FjU5WFqoSEKH3C
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1276224@bnt-lap001
Assistant-Session: 426ec497-e1c4-4dd3-b417-dfce1ca1dbc3
2026-09-09 14:42:20 +02:00
|
|
|
|
# security-layer-model v0.8 — ops-warden review
|
|
|
|
|
|
|
|
|
|
|
|
**Date:** 2026-09-09
|
|
|
|
|
|
**Reviewing:** `net-kingdom/canon/standards/security-layer-model_v0.8.md` (status
|
|
|
|
|
|
`proposed`, net-kingdom@66eeaba), circulated for assent by gate-house
|
|
|
|
|
|
**Prior reviews:** `2026-08-29-layer-model-v04-review.md`, `-v06-review.md`,
|
|
|
|
|
|
`-v07-scope-intent-assessment.md`
|
|
|
|
|
|
|
|
|
|
|
|
## The cell in question
|
|
|
|
|
|
|
|
|
|
|
|
v0.8 §6.4 obligation 3 adds: *unknown is not a zone and MUST resolve to
|
|
|
|
|
|
`fail_closed`.* `pep-stance.yaml` declares `unknown: fail_open` under `ADR-0009`.
|
|
|
|
|
|
§13.1 records the cell as non-conformant.
|
|
|
|
|
|
|
|
|
|
|
|
## Finding 1 — assent. The rule is right and our falsifier check fails.
|
|
|
|
|
|
|
|
|
|
|
|
gate-house wrote the falsifier into GH-DEC-2026-009's reversal: a scope that is
|
|
|
|
|
|
genuinely unknown **and** genuinely low-consequence, where failing closed costs
|
|
|
|
|
|
availability with no security gain. Their expectation is that any such path is a
|
|
|
|
|
|
§5.1 read-only diagnostic carrying no PEP obligation at all.
|
|
|
|
|
|
|
|
|
|
|
|
We looked for one on our side and do not have it. The stance map governs exactly
|
|
|
|
|
|
one protected action — `warden sign`, SSH certificate issuance. That is a
|
|
|
|
|
|
credential-issuing side effect, not a diagnostic; §5.1 does not reach it. So the
|
|
|
|
|
|
predicted escape hatch is absent here, and the argument stands unrebutted on its
|
|
|
|
|
|
own terms:
|
|
|
|
|
|
|
|
|
|
|
|
- §9.3 licenses trading availability for openness **per zone**, knowingly, for a
|
|
|
|
|
|
named scope. Where the scope is unknown that trade was never made for the
|
|
|
|
|
|
request in hand, so resolving permissively does not extend a considered
|
|
|
|
|
|
decision — it invents one.
|
|
|
|
|
|
- Unknown is the cheapest state to induce. Our own `unknown` reasons are
|
|
|
|
|
|
`workload_resolution_absent` and `catalog_applicability_absent` — an
|
|
|
|
|
|
unregistered workload or a missing declaration, neither of which requires a
|
|
|
|
|
|
credential to arrange.
|
|
|
|
|
|
|
|
|
|
|
|
We accept that being unclassifiable must not buy permissiveness. The cell is
|
|
|
|
|
|
wrong as doctrine.
|
|
|
|
|
|
|
|
|
|
|
|
## Finding 2 — the rule is right and adopting it today would be a global fail-closed flag in all but name
|
|
|
|
|
|
|
|
|
|
|
|
This is the finding we would like recorded, and it is about adoption cost, not
|
|
|
|
|
|
about the rule.
|
|
|
|
|
|
|
|
|
|
|
|
The stance map applies only when the engine is unreachable, so the cell's bite is
|
|
|
|
|
|
proportional to how much of the target population sits in `unknown`. Measured
|
|
|
|
|
|
today:
|
|
|
|
|
|
|
|
|
|
|
|
| Population | Resolved to a zone | `unknown` | `not-applicable` |
|
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
|
| Signing targets (actor resources in the flex-auth snapshot) | **0** | **3** | 1 |
|
|
|
|
|
|
| Routing catalog lanes | 3 | 18 | 12 |
|
|
|
|
|
|
|
|
|
|
|
|
Every signing target that resolves at all resolves to `unknown`. So
|
|
|
|
|
|
`unknown: fail_closed` adopted today does not fail closed on an edge case; it
|
|
|
|
|
|
fails closed on **essentially every certificate ops-warden issues** whenever
|
|
|
|
|
|
flex-auth is unreachable.
|
|
|
|
|
|
|
|
|
|
|
|
That is the precise configuration `ADR-0006` rejected and `ADR-0009` replaced: one
|
|
|
|
|
|
value making the decision engine a uniform dependency of every signing path,
|
|
|
|
|
|
*including the continuity paths needed to repair that dependency*. If flex-auth is
|
|
|
|
|
|
unreachable and the operator needs an SSH certificate to reach the host and
|
|
|
|
|
|
restore it, `unknown: fail_closed` denies the certificate — and the target is
|
|
|
|
|
|
`unknown` precisely because nobody has classified the repair path.
|
|
|
|
|
|
|
|
|
|
|
|
The zone model already answers this: classify continuity paths `z2-continuity`,
|
|
|
|
|
|
whose stance is `fail_open`, and the deadlock does not arise. We accept that as
|
|
|
|
|
|
the right answer. The point is that it is *work not yet done*, and the reason it
|
|
|
|
|
|
is not done is not ours to fix — 18 of our 18 unknown lanes are unknown because
|
|
|
|
|
|
another repo has not published an authoritative workload identity declaration.
|
|
|
|
|
|
`ADR-0009` rule 3 forbids us from closing that gap with inference, correctly.
|
|
|
|
|
|
|
|
|
|
|
|
So as written, v0.8 §6.4 obligation 3 transfers an availability cost onto the
|
|
|
|
|
|
consumer for a classification gap the consumer cannot close alone, and it does so
|
|
|
|
|
|
at the moment of assent rather than at the moment the gap closes.
|
|
|
|
|
|
|
|
|
|
|
|
**What we ask for**, in preference order:
|
|
|
|
|
|
|
|
|
|
|
|
1. **§6.4 obligation 3 names a transition.** A consumer may declare
|
|
|
|
|
|
`unknown: fail_open` as a dated, published transitional state with a
|
|
|
|
|
|
classification-coverage figure attached, converting to `fail_closed` on
|
|
|
|
|
|
coverage rather than on calendar. This keeps the doctrine and prices the
|
|
|
|
|
|
adoption honestly.
|
|
|
|
|
|
2. Failing that, **§13.1 records classification coverage alongside stance.** A
|
|
|
|
|
|
register row reading `unknown: fail_closed` while 100% of that consumer's
|
|
|
|
|
|
targets are unknown is conformant and materially misleading; a reader cannot
|
|
|
|
|
|
tell a strict consumer from an unclassified one. This is the same defect
|
|
|
|
|
|
§11's *published map equals shipped behaviour* rule exists to prevent, one
|
|
|
|
|
|
level up: the map is now accurate about itself and inaccurate about its
|
|
|
|
|
|
effect.
|
|
|
|
|
|
|
|
|
|
|
|
## Finding 3 — §12's derived-artifact rule, small and in agreement
|
|
|
|
|
|
|
|
|
|
|
|
Six instances in one week matches our experience. `registry/generated/high-risk-data-paths.yaml`
|
|
|
|
|
|
carries `catalog_revision` and a `catalog_dirty` flag for exactly the reason §12
|
|
|
|
|
|
now generalises: a derived artifact whose revision does not describe the file it
|
|
|
|
|
|
came from is worse than one with no revision, because the stale one is trusted.
|
|
|
|
|
|
No change requested; recording the agreement since the rule is being generalised
|
|
|
|
|
|
from other repos' incidents.
|
|
|
|
|
|
|
|
|
|
|
|
## What ops-warden is doing about it
|
|
|
|
|
|
|
|
|
|
|
|
Not flipping the cell on a `proposed` standard, and not flipping it before
|
|
|
|
|
|
coverage exists — that ordering is `ADR-0006`'s whole holding. `WARDEN-WP-0040`
|
|
|
|
|
|
records the adoption sequence: raise signing-target classification coverage
|
|
|
|
|
|
first, then convert the cell, with the deadlock case classified before rather
|
|
|
|
|
|
than after. If v0.8 is accepted with obligation 3 unchanged, ops-warden's cell
|
|
|
|
|
|
remains a **declared gap** under §11 — tracked non-conformance with a named
|
|
|
|
|
|
owner and a route, which is the mark §9.1 exists for and which we argued for in
|
|
|
|
|
|
the v0.6 round.
|
|
|
|
|
|
|
|
|
|
|
|
## Axis declaration (second change touching us)
|
|
|
|
|
|
|
|
|
|
|
|
`pep-stance.yaml` already carries `scope: security-zone`. No change needed. We
|
|
|
|
|
|
agree with rejecting a forced migration onto zones — secrets-engine scoping by
|
|
|
|
|
|
catalog stage is an accurate statement about their control, and making them
|
|
|
|
|
|
assert a zone they cannot know would trade a real fact for a conformant fiction.
|
|
|
|
|
|
The unavailability of cross-axis aggregation is better recorded than papered
|
|
|
|
|
|
over, and §13.1 now says so.
|
docs: mark the unknown cell, measure the coverage we asked to publish
gate-house ruled the v0.8 assent round (GH-DEC-2026-011, net-kingdom@64394e9):
ask 1 declined, ask 2 adopted.
Ask 1's refusal is accepted without reservation and the reason is better than
the ask -- a sanctioned transitional fail_open is indistinguishable at runtime
from the stance the rule forbids, and would make the rule optional at the only
moment it costs anything.
Ask 2 gave §13.1 a Coverage column with this repo's figures as its first
entries. Since we asked for the column, we owe it accuracy:
scripts/report_coverage.py measures both populations from the artifacts the
runtime uses (reusing the workload-join build rather than re-deriving it), and
a test asserts pep-stance.yaml's published block equals what it measures.
A hand-counted number in a register that explicitly does not recompute it
decays silently, and a stale figure beside a marked cell is worse than the
blank the other four rows carry.
pep-stance.yaml marks the unknown cell inline as a declared gap -- assent, the
measured reason for not flipping, the declined ask, WARDEN-WP-0040 as route --
and a second test keeps it marked while it is fail_open, failing when it is
flipped. standard_version stays 0.7 because that is what binds; v0.8 is
proposed, so it gains standard_version_reviewed rather than pre-adopting.
Separately, gate-house corrected GH-DEC-2026-008: the claim/decision digest
comparison it originally required is unimplementable and a fail-closed
consumer obeying it would have denied permanently. We had never copied the
wording, so nothing to unwind -- but everything they have sent about this lane
was living in an inbox thread, a bad home for a correction that only matters
when someone finally wires the consume. Now wiki/ApprovalConsumption.md,
leading with "nothing is wired", carrying the corrected target and the
attribution gap that digest matching does not discharge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EPuTc18FjU5WFqoSEKH3C
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1276224@bnt-lap001
Assistant-Session: 426ec497-e1c4-4dd3-b417-dfce1ca1dbc3
2026-09-10 08:02:10 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Ruling — GH-DEC-2026-011, 2026-09-09
|
|
|
|
|
|
|
|
|
|
|
|
gate-house closed the assent round the same day: **ask 1 declined, ask 2
|
|
|
|
|
|
adopted**, standard amended at `net-kingdom@64394e9`, dispositioned in
|
|
|
|
|
|
`gate-house/docs/conformance/2026-09-06-v08-assent-round.md`.
|
|
|
|
|
|
|
|
|
|
|
|
**Ask 1 (a dated transitional `unknown: fail_open`) — declined**, and the reason
|
|
|
|
|
|
is better than the ask. A sanctioned transitional `fail_open` is
|
|
|
|
|
|
*indistinguishable at runtime* from the stance the rule forbids, and it would make
|
|
|
|
|
|
the rule optional at the moment of adoption — the only moment it costs anything.
|
|
|
|
|
|
Dating an intention to stop being permissive does not make `unknown` any more
|
|
|
|
|
|
expensive for an attacker to induce while the transition runs. Accepted without
|
|
|
|
|
|
reservation; the argument defeats the ask on its own terms.
|
|
|
|
|
|
|
|
|
|
|
|
**Ask 2 (coverage published beside stance) — adopted.** §13.1 now carries a
|
|
|
|
|
|
Coverage column and this repo's figures are its first entries: *signing targets
|
|
|
|
|
|
0/3 resolved; routing lanes 3/21 resolved (2026-09-09, self-measured)*. Three
|
|
|
|
|
|
guards came with it, all of which we would have wanted: coverage does not soften
|
|
|
|
|
|
the stance, does not gate it, and never makes a non-conformant cell conformant; a
|
|
|
|
|
|
blank means "not reported" and never "complete"; and the decision carries an
|
|
|
|
|
|
explicit reversal condition — if a row is ever argued to be conformant *because*
|
|
|
|
|
|
its coverage is low, the column is doing harm and comes out.
|
|
|
|
|
|
|
|
|
|
|
|
That reversal condition is the right shape and it binds us first, since the column
|
|
|
|
|
|
exists because we asked. Our row reads non-conformant **and** low-coverage; those
|
|
|
|
|
|
are two facts, and the second is not a defence of the first.
|
|
|
|
|
|
|
|
|
|
|
|
**The deadlock was recorded rather than routed around.** §6.4 obligation 3 now
|
|
|
|
|
|
states that a stricter stance is not a licence to manufacture the membership that
|
|
|
|
|
|
makes it survivable — where a scope is unknown because another repository has
|
|
|
|
|
|
published no workload-identity declaration, the consumer MUST NOT infer one. That
|
|
|
|
|
|
is `ADR-0009` rule 3 promoted into the standard at exactly the point where the
|
|
|
|
|
|
pressure lands, which is more than we asked for.
|
|
|
|
|
|
|
|
|
|
|
|
### What we did in response
|
|
|
|
|
|
|
|
|
|
|
|
- `pep-stance.yaml` marks the `unknown` cell as a **declared gap** inline, with
|
|
|
|
|
|
the assent, the measured reason for not flipping, the declined ask, and
|
|
|
|
|
|
`WARDEN-WP-0040` as its route. The stance value is unchanged — v0.8 is still
|
|
|
|
|
|
`proposed` and v0.7 remains in force, so `standard_version` stays `0.7` with a
|
|
|
|
|
|
new `standard_version_reviewed: 0.8`.
|
|
|
|
|
|
- The coverage figures are published in `pep-stance.yaml` and, because we asked
|
|
|
|
|
|
for the column, **measured rather than hand-counted**:
|
|
|
|
|
|
`scripts/report_coverage.py` derives both populations from the artifacts the
|
|
|
|
|
|
runtime uses, reusing `report_workload_join.build` rather than re-deriving the
|
|
|
|
|
|
join. `tests/test_layer_conformance.py` asserts the published block equals the
|
|
|
|
|
|
measured one, and asserts the cell stays marked while it is `fail_open`.
|
|
|
|
|
|
|
|
|
|
|
|
A hand-counted figure in a register that explicitly does not recompute it would
|
|
|
|
|
|
decay silently, and a stale number beside a marked cell is worse than the blank
|
|
|
|
|
|
the other four rows carry — a blank at least reads as "not reported".
|
|
|
|
|
|
- `WARDEN-WP-0040` T04 is closed; T01–T03 stand unchanged. gate-house confirmed
|
|
|
|
|
|
they are not asking for the cell to be flipped before coverage exists.
|