312 lines
16 KiB
Markdown
312 lines
16 KiB
Markdown
---
|
||
id: WARDEN-WP-0044
|
||
type: workplan
|
||
title: "Follow through on KnownSecurityCases along their type's route"
|
||
domain: infotech
|
||
repo: ops-warden
|
||
status: active
|
||
flavor: implementation
|
||
owner: claude-code
|
||
topic_slug: netkingdom
|
||
created: "2026-10-08"
|
||
updated: "2026-10-11"
|
||
depends_on: []
|
||
related_repos:
|
||
- net-kingdom
|
||
- ops-mason
|
||
- key-cape
|
||
- railiance-platform
|
||
state_hub_workstream_id: "f7e81013-6868-5d99-8528-ba75a442eca9"
|
||
---
|
||
|
||
# WARDEN-WP-0044 — Follow through on KnownSecurityCases along their type's route
|
||
|
||
## Why
|
||
|
||
NetKingdom is introducing SecurityCaseTypes (net-kingdom `NK-WP-0048`, canon
|
||
`security-case-types_v0.1`, proposed). Once a case of an implemented type is
|
||
accepted and ops-mason has materialized its structure (`MASON-WP-0010`),
|
||
ops-warden carries out the remaining steps. These are the owners' declared entry
|
||
points, run on the authentication path the type defines, followed by the type's
|
||
verification.
|
||
|
||
Today ops-warden routes needs and, for `exec_capable` lanes, proxies fetches as
|
||
the caller. It does not yet know about cases, and each new integration ends with
|
||
a founder running a hand-written script (for example the proposed KEY-WP-0038-T02
|
||
rollout). This plan gives ops-warden the follow-through role, without widening
|
||
what it may do: each step still runs under its owner's entry point and the
|
||
caller's identity.
|
||
|
||
## Tasks
|
||
|
||
```task
|
||
id: WARDEN-WP-0044-T01
|
||
status: done
|
||
depends_on: [NK-WP-0048-T03]
|
||
priority: high
|
||
state_hub_task_id: "ef0920fc-418e-52cb-acff-9a5c0c728bf5"
|
||
```
|
||
|
||
### T01 — Define the type's follow-through section
|
||
|
||
Propose the `follow_through` part of the SecurityCaseType schema together with
|
||
net-kingdom. For each step it names the owner entry point, `warden_executes`,
|
||
the auth method and lane, whether an attended human login is unavoidable (with
|
||
the type's justification), and the verification command. Write it for the first
|
||
type, `oidc-browser-relying-party`, covering key-cape client registration,
|
||
custody minting and LLDAP group membership.
|
||
|
||
2026-10-08: NK-WP-0048-T03/T04 landed with a drafted section. Review is in
|
||
`history/2026-10-08-oidc-rp-follow-through-review.md`. It requires three
|
||
changes before T02: a per-step `lane`, a narrow `warden_executes`, and
|
||
`value_handling`. It also lists four refinements. This task is done when
|
||
net-kingdom acknowledges the review (NK-WP-0048-T05).
|
||
|
||
2026-10-09: R4 was withdrawn in favour of ops-mason's per-case
|
||
`{consuming_service}-keycape-client`, with the entry body templated in the type
|
||
(R4′, see the addendum in the review).
|
||
|
||
2026-10-09: the revised 1.0 type and canon now carry R1–R3, R4′, R5–R7.
|
||
`warden case check-type <type.yaml>` (`src/warden/case_type.py`, tests in
|
||
`tests/test_case_type.py`) checks a type's follow-through mechanically: the
|
||
live type is schema-conformant, with one attended session
|
||
(`key-cape-oidc-login`). It is not yet plannable, because two lanes are not
|
||
in the catalog: `identity-provisioner-groups` (net-kingdom) and
|
||
`keycape-client-secret-generate` (railiance-platform). These gate T02, not
|
||
T01. T01 still closes on net-kingdom's acknowledgement (NK-WP-0048-T05).
|
||
|
||
2026-10-09: ops-warden cannot add `identity-provisioner-groups` as the type
|
||
declares it. The provisioner accepts only user-engine's runtime token and
|
||
admits only user-engine pods, so no caller-identity path exists. R8 (review
|
||
addendum) asks net-kingdom either to move `ensure-groups` into the existing
|
||
attended session (recommended) or to give the provisioner an operator
|
||
credential. `keycape-client-secret-generate` needs a generate-in-custody entry
|
||
point from railiance-platform, and the broker does not have one yet. Both were
|
||
sent to their owners.
|
||
|
||
2026-10-09: done. net-kingdom took R8 option 1, so `ensure-groups` runs attended
|
||
in the `register-client` session and `identity-provisioner-groups` is dropped
|
||
(net-kingdom `0e86f95`). The founder acknowledged the type with memo r4 (hub
|
||
decision `e418263e`, NK-WP-0048-T05 done).
|
||
|
||
```task
|
||
id: WARDEN-WP-0044-T02
|
||
status: done
|
||
depends_on: [WARDEN-WP-0044-T01]
|
||
priority: high
|
||
state_hub_task_id: "5f68c991-f490-58f8-a121-92f74073d272"
|
||
```
|
||
|
||
### T02 — Plan a case run
|
||
|
||
`warden case plan <case.yaml>` checks that the case is accepted and materialized,
|
||
resolves each step to its catalog entry and entry point, and prints the run:
|
||
which steps warden executes, which run as the attended caller, what the
|
||
verification is. No secret values; no execution.
|
||
|
||
Tests: refuse an unaccepted or unmaterialized case; refuse a step whose catalog
|
||
entry is missing or not reviewed; show attended steps explicitly.
|
||
|
||
2026-10-09: done. `warden case plan <case.yaml> [--type] [--json]`
|
||
(`src/warden/case_plan.py`, 14 tests in `tests/test_case_plan.py`). It refuses:
|
||
- a case that is not `materialized`, or has no acceptance record
|
||
- a type that is mismatched or not acknowledged
|
||
- any `case check-type` finding, including a missing or unreviewed lane
|
||
- undeclared, type-fixed or secret-sensitivity parameters
|
||
- a placeholder the case does not set, or an unsupported `when`
|
||
|
||
It fills type defaults (`issuer`), evaluates `<param> == <value>` conditions,
|
||
renders owner, entry point, auth and ready_check, groups attended steps per
|
||
lane, and lists the applicable verification.
|
||
|
||
The live Forgejo case is correctly refused: it is `accepted`, not
|
||
materialized. A materialized copy, with a stand-in lane for
|
||
`keycape-client-secret-generate`, renders six steps: two attended steps plus
|
||
`register-client` in one `key-cape-oidc-login` session, one warden step
|
||
(`mint-secret`), and two owner steps. Seven checks follow.
|
||
|
||
Observation for net-kingdom (not blocking): `bind-env` runs before
|
||
`register-client`, in type order. Its entry carries a literal
|
||
`KEYCAPE_<CLIENT_ID>_CLIENT_SECRET`, which is not a `{param}` placeholder, so it
|
||
reaches the plan unrendered.
|
||
|
||
2026-10-09 follow-up:
|
||
- **Format specs.** `_render` supports net-kingdom's `{param:env}` spec (upper
|
||
case, `-` as `_`, the same as `keycape-client.py env_name`) and refuses any
|
||
other spec or `!conversion`.
|
||
- **Case reference.** The plan prints `case-ref: <case_id>@sha256:<hex>` over the
|
||
case file's exact bytes. This is the reference proposed to railiance-platform
|
||
for `credential.py generate --case-ref` (RPF-WP-0054-T01); the broker
|
||
re-derives it from the file.
|
||
- 18 tests now.
|
||
|
||
```task
|
||
id: WARDEN-WP-0044-T03
|
||
status: progress
|
||
depends_on: [WARDEN-WP-0044-T02, MASON-WP-0010-T03]
|
||
blocking_reason: "Local durable execution kernel and injected failure harness implemented. Native acceptance still requires authenticated owner machine descriptors/adapters, actual Forgejo materialization receipt, contained session integration, ordered verification and first native run."
|
||
priority: high
|
||
state_hub_task_id: "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89"
|
||
```
|
||
|
||
### T03 — Run and verify a case
|
||
|
||
`warden case run <case.yaml>` executes the plan. Owner entry points run through
|
||
the existing proxy as the caller; steps that need an attended login use the
|
||
existing contained login envelope (silent command, session revoked afterwards),
|
||
grouped into as few attended sessions as possible. Then it runs the type's
|
||
verification and writes a receipt without values. `warden case rollback`
|
||
reverses exactly this case's steps.
|
||
|
||
Acceptance: the Forgejo reference case (NK-WP-0048-T06) reaches `live` through
|
||
this path, with no case-specific script.
|
||
|
||
2026-10-17: original-intent lookup/context interface for NK-WP-0048-T15, source
|
||
and synthetic tests only. `src/warden/case_run.py`: `Executor.run` writes an
|
||
`OriginalIntent` (schema `warden.case.original-intent.v1`: the dispatched run
|
||
Invocation — attempt, step, owner, target, actor, principal, lane, all six pins,
|
||
tenant, realm — plus admitted adapter source) in the same SQLite transaction as
|
||
the pre-dispatch journal row. `Journal.original_intent(binding, current, source)`
|
||
returns it after restart and refuses on a missing record or any
|
||
pins/source/step/owner/target/actor/principal/lane/tenant/realm mismatch, before any
|
||
journal write, so the step stays uncertain. An adapter's
|
||
`reconcile_original(ReconcileContext(original, attempt))` receives the original
|
||
intent separately from the new reconcile attempt. This is Warden's record of what
|
||
it dispatched, not owner created-only provenance. Current Executor.admit leaves
|
||
tenant/realm empty; the reserved coordinates establish no admitted provenance.
|
||
Owner descriptors/adapters, contained session integration, Forgejo
|
||
materialization and the native run remain open.
|
||
|
||
2026-10-17 source consistency follow-through: retained the additive intents table
|
||
and reconcile_original callback and fixed four reproduced local defects: original
|
||
actor mismatch, payload/SQL attempt-ID mismatch, orphan intent without its
|
||
matching original running receipt, and arbitrary nonmetadata values accepted by
|
||
the original-intent serializer. Exact bounded field validation, duplicate-free
|
||
bounded decode and save-time intent/invocation/running-state equality now preserve
|
||
the original receipt linkage before owner lookup. No new context store, native
|
||
verifier, trust root, adapter activation or authority migration was introduced.
|
||
|
||
The actual canonical focused kernel and new consistency suite passes 54 checks;
|
||
owner-installed Ruff check/format passes. The existing generated-data/synthetic
|
||
owner fixture and a distinct interpreter prove source correspondence/refusal,
|
||
not independent native custody or creation. Earlier comparison probes and failed
|
||
preparation are retained. Current caller and fresh session/grant revocation checks
|
||
remain required; original owner payload/revocation, exact native readback and
|
||
created-only provenance still need independent authenticated owner admission.
|
||
No JSON/SQL context, unknown tenant/realm, changed run-sheet or legacy missing
|
||
intent supplies recovery, replay or undo authority. T03 remains progress and all
|
||
native acceptance gates/statuses/dependencies/UUIDs remain unchanged. Evidence:
|
||
Custodian original-context-compare-r2 and original-context-owner-followthrough-r3
|
||
under docs/changes/netkingdom-target-2026-10-17/; publication/projection is separate.
|
||
|
||
Agreed with railiance-platform (RPF-WP-0054-T01, 2026-10-09):
|
||
- `mint-secret` runs `credential.py generate --grant keycape/client-secret-generate
|
||
--client-id <id> --case-ref <case-ref> --case-file <case.yaml>` as the caller's
|
||
child process. The broker re-derives the case-ref and checks status and
|
||
client_id.
|
||
- warden refuses that step when `WARDEN_AGENT_ID` is set.
|
||
|
||
2026-10-10: railiance-platform implemented the command (RPF-WP-0054-T03). It is
|
||
catalogued as draft `keycape-client-secret-generate`, with `exec_owner`
|
||
railiance-platform and `exec_capable: false`. `warden case check-type` now
|
||
reports the lane as LANE-UNREVIEWED rather than LANE-MISSING. Before it can be
|
||
activated:
|
||
- the founder binds the issuer policy to the operator OIDC role
|
||
- key-cape confirms the secret format
|
||
- the RPF-WP-0054-T05 probe passes
|
||
|
||
2026-10-10 (later):
|
||
- **Broker lane.** The issuer role is bound (CCR-2026-0043) and the T05 probe
|
||
passed (railiance-platform `21b8af3`). Only key-cape's format confirmation is
|
||
left. The playbook now includes the issuer-role login, a 15m token with
|
||
unlimited uses that `warden case run` must group with the attended session.
|
||
- **Env spec.** net-kingdom adopted `{client_id:env}` as a 1.0 patch
|
||
(`9a9b8e3`). The plan renders `KEYCAPE_FORGEJO_CLIENT_SECRET`.
|
||
- **Forgejo case.** Rendered and built by ops-mason. It is not yet
|
||
`materialized`: MASON-WP-0010-T03 waits on the railiance-platform CCRs and
|
||
their founder apply.
|
||
|
||
2026-10-10: `keycape-client-secret-generate` activated on railiance-platform's
|
||
go-ahead (message 20973a9c) after key-cape confirmed the format (4e0213b3).
|
||
`warden case check-type` on the live type now reports `plannable=True`. The
|
||
only thing T03 still waits on is the Forgejo case reaching `materialized`
|
||
(MASON-WP-0010-T03).
|
||
|
||
2026-10-10: net-kingdom moved the type to 1.1 (`e2174ce`) and re-pinned the
|
||
Forgejo case. 1.1 adds the verification `consumer-enforces-groups` and requires
|
||
the hand-over to enforce `access_groups`. `check-type` on 1.1 reports
|
||
`plannable=True`, and a materialized copy renders six steps and the new check.
|
||
The case-ref digest covers the whole case file, including `status`, so it
|
||
changes when the case becomes `materialized`. `warden case run` therefore takes
|
||
it from the file at run time, and the broker re-derives it from the same file.
|
||
|
||
```task
|
||
id: WARDEN-WP-0044-T04
|
||
status: wait
|
||
depends_on: [WARDEN-WP-0044-T03]
|
||
blocking_reason: "Catalog integration follows a proven run."
|
||
priority: medium
|
||
state_hub_task_id: "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e"
|
||
```
|
||
|
||
### T04 — Catalog entries from types
|
||
|
||
Let a type contribute reviewed catalog entries once, so that every case of that
|
||
type routes without a new catalog edit. Cases add parameter values, not routes.
|
||
`warden route find` should be able to answer "which SecurityCaseType covers this
|
||
need?" before anyone builds by hand.
|
||
|
||
|
||
2026-10-10 consolidation source prototype (not an accepted owner adapter API):
|
||
`src/warden/case_receipt_state.py` provides a pure immutable receipt reducer,
|
||
with exact case/type/adapter/catalog/acceptance/run-sheet pins, running intent
|
||
before dispatch, uncertain recovery after a lost result, no success replay,
|
||
explicit retry-safe failed-before-change retry and created-only reverse undo.
|
||
It executes nothing and accepts no command output. The caller must durably
|
||
persist running before any future side effect; storage/authenticated owner
|
||
receipts/live adapters are not implemented. Focused offline tests cover changed
|
||
pins, lost receipts, retry/refusal, uncertain rollback and shared-object
|
||
preservation. Source proposal: `docs/security-case-execution-proposal.md`.
|
||
|
||
T03 remains open: typed owner execution/receipt contracts require review, the
|
||
Forgejo structural materialization needs owner receipts, and CLI dispatch,
|
||
contained sessions, native verification and provenance remain unimplemented.
|
||
There is a circularity to resolve with net-kingdom/ops-mason: materialization
|
||
cannot require mint-secret if mint-secret is itself a follow-through step whose
|
||
planning requires materialized. No case status is changed by this prototype.
|
||
|
||
|
||
2026-10-11: the exact execution-design memo version1 has confirmed native human
|
||
acceptance (approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca). The approved proposal
|
||
bytes remain unchanged. `src/warden/case_run.py` now implements a closed v1 local
|
||
execution seam, independent admission/evidence callbacks, private transactional
|
||
SQLite receipt storage, process locking, running-before-dispatch, crash recovery
|
||
to uncertainty, explicit safe retry, separately required broker-grant and parent-session revocation
|
||
proofs, native owner reconciliation and created-only
|
||
reverse rollback. Thirty-seven kernel tests and fourteen reducer tests pass;
|
||
ruff checks pass. See `docs/security-case-execution-kernel-v1.md` for exact scope
|
||
and receipt provenance. No installed native adapter or live CLI execution is
|
||
claimed. Current prose types stay plan-only; acknowledged descriptor/type machine
|
||
seams and authenticated owner/caller receipts are still required. T03 progresses
|
||
for implementation but cannot finish before ordered native Forgejo acceptance.
|
||
|
||
2026-10-16 source preparation (isolated checkout; no native acceptance):
|
||
T03's browser parameter mismatch now has a producer-validator-backed projection
|
||
in `case_inputs.py`, explicitly configured on the real Executor. Exact 1.1/1.2
|
||
parameter definitions and all four reviewed producer modules are pinned; full case
|
||
validation admits reviewed security-sensitive metadata while unknown/fixed/secret
|
||
inputs and type/producer drift refuse. Only the reviewed descriptor target
|
||
reaches the owner callback; no parameter journal payload is added. Actual
|
||
Executor/producer tests cover positive versions and refusal/containment paths.
|
||
Acknowledged machine descriptors, authenticated owner evidence, real Forgejo
|
||
materialization and contained native sessions remain mandatory and unimplemented;
|
||
T03 stays progress, T04 stays wait. No type or case status changed.
|
||
|
||
2026-10-16 producer source refresh: independently reviewed NetKingdom
|
||
`015694ee414c802dc2ef1fa0120dfed610086e69` optional descriptor-map repair.
|
||
Only the validator/evidence source pins change; exact parameter and transitive
|
||
source identity remain enforced. Actual canonical producer, Executor and reducer
|
||
suite passes 87 checks, including 14 new malformed-map integration cases; all
|
||
four missing/changed modules still refuse. Review:
|
||
`docs/reviews/2026-10-16-browser-producer-pin-refresh.md`. No adapter attestation,
|
||
native acceptance, credential use or privileged dispatch. T03 remains progress.
|