the-custodian/history/20261003-security-gate-packets.md
codex 0aee8dc727
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
CUST-WP-0075: ranked decision packets, sweep results, qualifier sync gap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:46:36 +02:00

8.5 KiB

id type title date assessor audience status related_workplans origin
20261003-security-gate-packets assessment What the dependency graph says about how security work gets blocked, and what to change 2026-10-03 the-custodian (Claude) founder recorded
CUST-WP-0075
CUST-WP-0074
CUST-WP-0072
RPF-WP-0035
WARDEN-WP-0027
Founder request 2026-10-03: use the dependency facility to shrink the blocked set and learn how to make the security infrastructure easier to maintain and extend.

What the dependency graph says about security work

Snapshot 2026-10-03 from the workplan files of 145 registered repos (docs/evidence/2026-10-03-dependency-graph-triage/). This is an assessment with proposals. It changes no canon and authorizes nothing.

1. The shape of the blockage

Measure Value
Open workplans 108 (100 blocked)
Wait tasks inside them 232
Human gates (needs_human: true) 69
Human gates that named a hub decision 0
Owner-return waits (blocked_on: message-from:) 20
Wait tasks whose blockers were already terminal 8
Cycles 0

Three facts matter more than the counts.

The graph has no cycles and few true roots. Removing the already- satisfied and mis-statused cases, the open set hangs off roughly a dozen root nodes. The two largest, SAND-WP-0015-T04 (7 workplans behind it) and SECRETS-WP-0009-T03 (5), are themselves owner-return waits on railiance-platform and glas-harness, which in turn wait on one attended OpenBao session. So the single largest lever is not a repo and not a workplan; it is one kind of act by one person.

Human gates were invisible to the hub. The wait-qualifier standard (canon/standards/task-wait-qualifiers_v0.1.md) and C-40 rule 4 already let a resolved decision clear a task. Nobody used it: all 69 gates were prose. Bernd therefore had 69 places to read and no list to resolve. CUST-WP-0075-T03 routed 68 of them to eleven hub decisions (one has no stated gate at all, WARDEN-WP-0037-T03, and goes back to its owner).

Security work is where this concentrates. Of the 69 gates, 19 are attended OpenBao provisioning acts (packet D1), 14 are the ArgoCD production-ownership sequence (D2), 3 are trust-root and custody design (D3), 3 are destructive cleanups behind retention windows (D4), 7 are production risk acceptances (D5). That is 46 of 69 in the credential, identity, delivery and custody space. Product and research gates are the minority.

2. Why credential work blocks the way it does

Reading the lane documents in railiance-platform (docs/credential-lane-designs/, docs/openbao-approved-automation-delegation.md, docs/credential-lane-lifecycle-runbook.md) and the ops-warden catalog (38 routing entries) gives a consistent picture:

  1. Metadata is delegated, values are not. The delegated applier may write policies and auth roles from an approved CCR; secret values stay an attended operator act "unless a later workplan approves a stronger dual-control flow". Almost every D1 task is blocked at exactly that value step: the policy is designed, reviewed, often dry-run, and then waits for a session.
  2. Each lane negotiates its own session. Nineteen tasks in twelve repos describe the same act in their own words, with their own evidence expectations. Nothing aggregates them, so no session is ever "worth" scheduling and each lane waits for its own.
  3. Owner-return waits chain across repos. sand-boxer waits on railiance-platform, which waits on secrets-engine, which waits on the session. Each hop is a hub message that may or may not be answered; the C-25 unread-reply check exists but nothing sweeps it.
  4. The read model lags the files. The hub carried 201 dependency edges where the files carry more; task-level edges from the September backfill were "parked" behind a deploy. Anyone querying the hub for the graph got a thinner picture than the files hold.

None of this is a design flaw in OpenBao, KeyCape or ops-warden. It is the absence of a batching and routing layer between well-designed lanes and one scarce person.

3a. Two failure modes found while acting on the graph

Prose-only blockers defeat C-40. Three of the eight "satisfied" waits were not satisfied. Their depends_on named an old in-repo predecessor; the real gate (GLAS-WP-0012, an unread canon registration, an independent human author) lived only in the dated notes below the task. C-40 can only clear what was encoded, so it reported them as clearable. The fix was to encode the real edge, not to clear the task. Rule worth adding to the standard: when a dated note re-blocks a task, the same edit updates depends_on / needs_human / blocked_on.

Owner returns arrive and are not read. Seventeen owner-return waits had an unread reply from exactly the agent they wait on. One of them (COORDINATION-WP-0004-T02) was fully satisfied for a week: info-tech-canon finished INFO-WP-0030 on 2026-09-27 and sent the artifact id; nobody read it. Closing it took ten minutes. This is the strongest argument for P5 below, and it is not specific to security work, although most of the seventeen sit in security repos (ops-mason, gate-house, user-engine, railiance-platform).

3. Proposals (ease of maintenance and extension)

P1 — Gates are born routed. Extend C-39: a needs_human: true wait in an open, non-residual workplan without decision_id is a WARN. Owner repos then attach a decision at the moment they create the gate, and the hub's open-decision list is Bernd's queue. (state-hub, small.)

P2 — The attended provisioning window is a standing event. Instead of per-lane sessions, one dated window (fortnightly is enough at current volume). Its checklist is generated, not written: every task carrying the window's decision_id, joined to its CCR and lane doc, rendered by credential-change.py as a numbered attended sequence with the receipt each owner needs. Owners prepare to that checklist; the session runs it top to bottom; resolving the decision raises C-40 on every task. (railiance-platform owns the renderer; the-custodian owns the cadence.)

P3 — Decide the dual-control value path once. The delegation doc leaves "stronger dual-control" open. The honest options are (a) keep values attended but batched (P2 alone), (b) response-wrapped delivery where the issuing system writes the value into OpenBao under a one-time wrapping token the operator only unwraps, or (c) a scoped operator-write role under MFA used by a wrapped command that never prints. Pick one in packet D3 alongside the trust-root ruling; it removes the value step from most future lanes. (railiance-platform workplan.)

P4 — Catalog lane as the unit of extension. A new consumer today costs a design workplan, a CCR, an attended apply and bespoke evidence. Make the ops-warden catalog entry the template: entry + CCR skeleton + policy/role rendering + ExternalSecret manifest + evidence script scaffolded from one command. Then a new lane is a reviewed PR, not a workplan, and only its value step needs the window. (ops-warden + railiance-platform.)

P5 — Owner-return sweep. A daily check lists every blocked_on: message-from:<agent> task whose awaited agent has sent the waiting repo a message newer than the task's last note, read or not, and wakes that repo's agent to act on it. C-25 already watches unread replies; promote its finding from INFO to a work item. The 2026-10-03 sweep script is in the triage evidence folder.

P6a — Fix the qualifier sync gap (found 2026-10-03). After the routing commits, statehub fix-consistency --repo <slug> reported PASS in every repo, yet the hub held the decision_id for 1 of 58 routed tasks. Running scripts/consistency_check.py --repo <slug> --fix directly from the state-hub checkout raised C-41 and synced them. Same interpreter, same checker file, different working directory; the cause is not isolated. Until it is, a routing change is not visible to the hub, so P1 and P2 would act on stale data. (state-hub, high.)

P6 — Keep the hub graph current. Run statehub fix-consistency --all --remote on railiance01 nightly so C-20 keeps the edge table equal to the files, and finish the deploy that carries task-level edges. The graph scripts in this triage can then run against the hub alone.

4. What this does not change

The hub stays a read model. Decisions were recorded through the sanctioned route and every decision_id lives in the owner file, synced by fix-consistency. No workplan or task was registered by hand. No credential was requested, printed or moved.