Updated by fix-consistency on 2026-08-28:
- update .custodian-brief.md for ops-warden
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
railiance-platform accepted ops-warden revision 0fae0904 on 2026-08-23, in
RPF-WP-0017 (status: finished), together with railiance-infra approval at
186b030 and all five acceptance criteria met. T02 has been sitting `progress`
on a gate that was already open.
Verified rather than trusted: the receipt at bc1966da hashes to
d2ba444ed16989590325697e69d25283dc75a9432c29a72e627e80bf9fd987e4, matching
their record exactly.
One reason it went unnoticed is an identifier mismatch — T02 cites the
remediation interface as RAILIANCE-WP-0026-T01, but it is RPF-WP-0017-T01 in
the owner repo, and the cited id resolves to an unrelated workplan there.
Their acceptance is source acceptance only and authorizes no live drill, so
T02 stays progress: what unblocks is preparing a NEW scenario, which needs a
fresh human GO and is the platform owner s to execute. Surfaced, not taken.
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
T04 was the last open task, waiting on key-cape to accept or refuse ownership of
the coding-agent OpenBao issuance identity. They accepted, in KEY-WP-0009-T03,
on 2026-08-23: codex-railiance-platform is published in their
config/service-clients.example.yaml with subject service:codex:railiance-platform,
role coding-agent, scope openbao:login, 15m lifetime, and the service-auth
semantics in docs/openbao-service-auth-contract.md. The split is the one we
routed for — KeyCape issues, railiance-platform binds the OpenBao role, OpenBao
enforces, no secret value in either repo.
We found it by reading their repository. KEY-WP-0009-T04 records replying to
ops-warden; the inbox has zero messages from key-cape, read or unread. The task
sat `wait` on an answer that already existed.
That is T05's own lesson arriving on T04: a blocker is a claim about the world at
a date. So the same pass re-verified the two lanes pointing at key-cape against
their source instead of bumping dates:
- rapp-qonto-keycape-client -> verified: source-read. KEY-WP-0009-T02 did add
bounded service-auth, but that is client_credentials JWT issuance for OpenBao
machine login and does not front this client_secret_basic exchange or its
rotation. Blocker stands, now with evidence rather than memory.
- key-cape-oidc-login -> asked of key-cape today, which the entry had recorded
as still outstanding since 2026-08-21.
Also cleared the inbox that hid this: 9 stale unread, all superseded by shipped
work, with late closes sent to secrets-engine and llm-connect on the two threads
that had asked ops-warden something and never got an answer.
391 tests pass, ruff clean, boundary coverage 0 uncovered.
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
c374d41 added net-kingdom-lldap-bind-credential and
net-kingdom-privacyidea-admin-token as `risk: high` and did not re-run the
emitter, so registry/generated/high-risk-data-paths.yaml still described the
catalog at 0fae090. railiance-platform consumes that file instead of
hand-maintaining its deny list, and it has been reading a census two lanes short
since 2026-08-23.
This is precisely the drift WARDEN-WP-0033-T03 built the guard for — a lane
graded high after the last emit silently failing to reach the consumer. The
guard fired; nothing had acted on it.
The deny list itself does not move: both lanes are blocked on their OpenBao path
being published, so they land in `no_concrete_path` and concrete_path_count
stays 14. What changes is the count the consumer sees — 23 high-risk lanes, two
of which have no address yet. That is the honest signal and the reason the
bucket is listed rather than omitted.
check_agent_read_boundary.py still reports 0 uncovered.
The workload-join census moves 9 -> 11 not-applicable: both lanes are
provider/control-plane credentials rather than workload delivery lanes.
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
gate-house asked ops-warden to assent to three boundary items ratified in
GH-DEC-2026-001. All three are assented in ADR-0010.
Staff: accepted. Grepping section 5 as it invites turned up a real
non-conformance — src/warden/vault.py is a direct OpenBao client performing a
write, and so is `warden desk`'s `bao kv put`. Section 5's only escape hatch is
read-only diagnostics, which does not cover a signing write, so both are
declared in INTENT.md as an engine gap with intended owner secrets-engine and
the blocker "no engine exposes an SSH-CA surface" — ADR-0003 turned inward
rather than an exemption argued for. taint.py is metadata-only and declared
under the read-only allowance; `warden access` proxies run under the caller's
identity and supply no authority of their own.
Doctrine versus runbook: accepted. NetKingdom Security Literacy becomes a lane
routing runbook that references gate-house doctrine instead of restating it. It
had also become a prose second source for registry/routing/catalog.yaml, which
ADR-0001 already rules against.
Lane versus rule: assented unconditionally, and the access-engine veto is not
exercised. One request on sequencing only — a window where both names resolve.
gate-house added to the routing tables in INTENT.md and SCOPE.md.
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
Regenerated by fix-consistency; adds the inbound assent intake.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2564823@bnt-lap001
Assistant-Session: 2a7ed827-4928-4b9f-8613-9135c9cadfe9
The layer model is now published as
net-kingdom/canon/standards/security-layer-model_v0.1.md (proposed) and
ratified by gate-house GH-DEC-2026-001. The note previously said the
standard was not yet written.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2564823@bnt-lap001
Assistant-Session: 2a7ed827-4928-4b9f-8613-9135c9cadfe9
Records this repository's layer in the NetKingdom IT-security layer model
(Taxonomy / Tooling / Engines / Staff) and what should change in this INTENT
as a result. Links to the review that established the model:
gate-house/history/2026-08-28-security-layer-model-and-gate-house-recut.md
The note flags pending adaptation only; the body is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2564823@bnt-lap001
Assistant-Session: 2a7ed827-4928-4b9f-8613-9135c9cadfe9
The live-file pass missed these: archived ad-hocs carry a YYMMDD- filename
prefix, so the ADHOC-* glob did not match them. They still derive from the
forge, so they are live records rather than dead files.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
`ADHOC-YYYY-MM-DD` is unique per date but not per repository, so any two repos
opening an ad-hoc on the same day collide. The 2026-08-26 fleet projection
reset refused 9 records for exactly this reason.
Canon (work-record-types_v0.1, CUST-WP-0066) settled the form as
`{PREFIX}-WP-ADHOC-YYYY-MM-DD`, filename unchanged, and grandfathered existing
ids on the condition they are never *silently* re-derived. This is the explicit
migration that clause allows for.
The hub id is derived from the record id, so a changed id is a different
record: stale state_hub_*_id fields are dropped and fix-consistency re-derives.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
Updated by fix-consistency on 2026-08-25:
- update .custodian-brief.md for ops-warden
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
These workplans exist only in the retired local hub. Their random pre-ADR-007
identifiers are refused by C-06 as stale references, so they cannot be
registered. Deriving from the canonical record id takes no identity from
anything: central does not hold them and the old ids die with the cache.
Records central already holds were deliberately left untouched.
Refs CUST-WP-0068-T06
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
Updated by fix-consistency on 2026-08-25:
- update .custodian-brief.md for ops-warden
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
The file carried an identifier minted by the retired local hub while central
held a different record for the same file — ADR-010's same-filename,
different-UUID duplicate-registration class.
Central is authoritative (ADR-010 decision 1) and decision 6 says to mitigate
by changing the repository file rather than editing central, so the file adopts
central's id. The cache-side id dies with the cache.
Refs CUST-WP-0068
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
The remote row pointed at 127.0.0.1:18000, a reverse tunnel back to the
workstation. On railiance01 the State Hub runs in the cluster on that same
machine, so the request left the box and came back to reach a local service.
Refs CUST-WP-0067-T07
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
risk-nexus accepted the offer to match their convention rather than grow a second
one, and published it: 14d critical/high, 30d medium, 60d low, nothing auto-closing
on staleness alone. Their preference — point warden route gaps at those windows and
the two registers agree without a shared mechanism — is better than a joint tool.
blocker_stale_days() now maps lane risk onto those windows. A flat 14 would have
been wrong in both directions: too aggressive for a low-risk pointer, and it treated
an admin PAT lane the same as one.
ungraded takes the shortest window, not the longest. ADR-0007 makes an absent grade
a defect and ADR-0008 makes a grade cover the whole path, so a lane nobody graded is
the one whose blocker deserves least trust. Encoding that as 60 days would have been
the fail-open default this repo already fixed once.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 90-day --stale-days default on `warden route gaps` was not a loose threshold,
it was an inert one: the delegation register was created 2026-08-15, so it could
not have fired before November. It was inherited from the catalog pointer cadence
and applied to a claim with a completely different half-life.
Two changes. DEFAULT_BLOCKER_STALE_DAYS = 14 now governs interim blockers, while
DEFAULT_STALE_DAYS = 90 keeps governing pointer freshness -- "is this the right
owner and page" is quarterly, "has the owner answered" is not. 14 is calibrated
on blockers that actually cost something: ten days for the secrets-engine lanes,
one for RISK-F-0001, roughly fifty for FLEX-WP-0007.
The second change matters more. `reviewed` records when someone touched an entry,
which is indistinguishable from re-checking it -- six lanes read as freshly
reviewed today because I typed in them. `verified:` now says how the claim was
established, and asked-and-waiting explicitly does NOT count: that is the state
the secrets-engine blocker sat in for ten days while looking current. A lane in
that state is stale at zero days old, and key-cape-oidc-login proves it works.
8 of 14 interim lanes are honestly marked unverified rather than given a fresh
date they did not earn.
--fail-on-stale exits 3 for a cron or gate. No CI test on age: a date-triggered
failure breaks the build for whoever commits next instead of whoever owns the
blocker. The CI test is structural -- every interim lane must record how it was
verified -- so it fails on the commit that introduces the omission.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_catalog_gaps_lists_only_interim asserted openbao-api-key was an interim
cover. It encoded the same classification the entry did, so it defended the
wrong answer rather than catching it -- the second time this session a test has
held a judgement still (see ADR-0008).
The staleness test added in T03 did its job on the first real change: it failed
the moment the catalog moved without the generated artifact being re-emitted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three fixes, the first of which is my own botched edit from an hour ago.
1. The "six delivery modes" claim was never actually removed. I ran a str.replace
with no assertion on the result, it matched nothing, and the print said "ok".
The wrong claim sat in all seven lanes while I told secrets-engine it was
fixed. This edit asserts 5/1/1 and fails loudly otherwise.
2. Five lanes ACCEPTED (SECRETS-WP-0006, decision ae676382). The blocker now
records that secrets-engine holds the entry and that the interim proxy retires
on approved native verification in T05 -- not on acceptance. Retiring early
would drop the cover before the replacement is proven.
3. Two lanes REFUSED, and both refusals are right:
- key-cape-oidc-login: intended_owner corrected secrets-engine -> key-cape.
Login and identity-token issuance were never secrets-engine's. Not yet asked
of key-cape, and the record says so.
- openbao-api-key: not a delegable lane at all -- a <domain>/<workload>/<bundle>
routing template, not one secret lane, so there is no front door to own.
interim -> native with railiance-platform named: OpenBao is itself the front
door and this entry is a pointer to it, never an interim cover. It should
not have been inflating the interim count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Routed, not absorbed. It is an identity and issuance question about a principal
authenticating to OpenBao, which is key-cape's. secrets-engine drew the same
boundary at us an hour ago over key-cape-oidc-login and we agreed with them.
Named zone-engine as interested and user-engine as explicitly not involved, so
neither is concluded by inference. Asked for accept or refuse; a refusal with a
pointer is an equally good answer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
railiance-platform asked for a generated list to consume instead of hand-
maintaining agent-high-risk-boundary. Hand-maintaining it is what let the two
lists drift for four lanes in RISK-F-0009.
19 high-risk lanes, 14 concrete data paths, 5 without a single KV address listed
separately so absence does not read as omission. Carries catalog_revision and a
dirty flag. fields is null where unestablished, never a one-element guess.
The header states plainly that this is an input and not a policy: railiance-
platform owns the deny set and may deny more, less, or dispute a grade. ADR-0002
survives the handoff.
Two CI tests guard staleness, because a consumer applies this to a live control.
Note the immediate consequence of T02: 2 uncovered against a policy they closed
to 0 yesterday.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
secrets-engine reviewed our catalog metadata while drafting their five entries
and graded issue-core-ingestion-api-key and reuse-surface-hub-write-token high.
We had both as standard, and had deliberately regraded them DOWN on 2026-08-19.
They are right. Both paths carry a second credential our grade never looked at --
GITEA_BACKEND_TOKEN (CCR-2026-0002, a deliberate field-set decision) and a
dual-consumer webhook HMAC (CCR-2026-0005). Neither is recovered by rotating the
credential the lane is named after.
The defect is structural: we graded the lane by its headline field, but a read
returns every field at the path. Worse, the evidence was already in the CCRs we
cite as authoritative -- not missing, unread -- and a test asserted the wrong
answer, so a correct first-pass grade got overruled by it.
ADR-0008 records the rule: a grade covers every field its path discloses.
ADR-0007 is unchanged and still governs; this says what the grade is of.
Six of the remaining standard lanes have no KV path. Two have paths and no field
evidence; per ADR-0008 they are stated as unknown rather than assumed, and left
for operator-sanctioned grading.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
secrets-engine accepted five interim lanes and drafted the entries itself rather
than wait for our contribution. Reviewing them turned up a defect in our own
grading model, which is now T02 and the most consequential item in the plan.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>