diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/current-driver-patch-selection.json b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/current-driver-patch-selection.json new file mode 100644 index 00000000..b8ca7f7c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/current-driver-patch-selection.json @@ -0,0 +1,10 @@ +{ + "root_review_candidates": [ + "glas-harness-driver-reference.patch", + "sand-boxer-current-driver-reference.patch" + ], + "superseded_broad_sand_proposal": "sand-boxer-driver-reference.patch is retained but not recommended; it also changed historical prose references. Use the current-only patch instead.", + "preserve_historical_sand_prose": true, + "canonical_driver_edits_performed": false, + "frozen_receipts_unchanged": true +} diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/glas-harness/workplans/GLAS-WP-0012-first-local-profile-production-proof.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/glas-harness/workplans/GLAS-WP-0012-first-local-profile-production-proof.md new file mode 100644 index 00000000..221e6c3a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/glas-harness/workplans/GLAS-WP-0012-first-local-profile-production-proof.md @@ -0,0 +1,450 @@ +--- +id: GLAS-WP-0012 +type: workplan +title: "Prove the first local rein profile end to end" +domain: infotech +repo: glas-harness +status: blocked +flavor: implementation +owner: codex +topic_slug: first-local-profile-production-proof +created: "2026-09-05" +updated: "2026-09-27" +state_hub_workstream_id: "170bf1ae-337f-5553-8d1e-03b07100e08f" +--- + +# Prove the first local rein profile end to end + +## Context and scope + +Follow up the Claude-route portion of live residual `GLAS-IN-0002`, originating +in finished `GLAS-WP-0005`. Boundary hardening is committed at `63a7f9f` and +proved with a deterministic non-secret dispatch. That proof did not execute +the real rein CLI or make a model call. + +Start from `harness.agent-dev-local@1.0.0` (rein-aharness, +`claude-sonnet-4-6`, `claude-code-cli`, `green-commit-only`). Review a new +versioned candidate after the owner runtime contract is available. Keep the +existing blocked selection as the rollback pin. The open-weight profile and +remote profile do not acquire readiness from this workplan. + +Glas owns profile selection, its adapter, and acceptance evidence. Sand-boxer +owns runtime provisioning, execution environment, isolation, egress enforcement, +and teardown. The selected rein and credential owner own authentication and +credential acquisition. Use existing owner work `SAND-WP-0014-T05`; its current +OpenRouter dependency must be distinguished from this first Claude-route proof. + +## Establish the exact local baseline and acceptance requirements + +```task +id: GLAS-WP-0012-T01 +status: done +priority: high +state_hub_task_id: "711aeb7e-4664-5078-8c65-3fe6f46f7999" +``` + +Completed 2026-09-05. Reviewed Glas `63a7f9f`, sand-boxer `b6655d8`, and +rein-aharness `1429db5`. Added a reproducible non-secret owner-execution runtime +inventory and the [acceptance contract](../docs/local-profile-acceptance.md). + +The baseline can discover Python, Git, and a `claude` command. It cannot +discover `rein-aharness`, its Python module, or `llm_connect`. Command discovery +alone does not prove that Claude can start or authenticate. The namespace has +only loopback; declared egress and credential route references are empty. +Owner execution and workspace teardown pass. The baseline is therefore +insufficient for a real rein run. + +Reviewed the plan against current implementation. Local preparation is done; +remaining execution waits on the owner inputs in T02. + +## Resolve and verify the owner runtime, credential, and egress contract + +```task +id: GLAS-WP-0012-T02 +status: wait +depends_on: [SAND-WP-0014-T05] +blocking_reason: "Dependency: sand-boxer SAND-WP-0014-T05 with rein-aharness and its credential owner." +priority: high +state_hub_task_id: "4209f564-a9ee-57f9-9522-3708988801ef" +``` + +Dependency: sand-boxer `SAND-WP-0014-T05`, with rein-aharness and its credential +owner. Required deliverables are listed in `docs/local-profile-acceptance.md`: + +- Exact runtime artifact/revision, in-sandbox entrypoints and dependencies, + sanitized environment, private writable state, and deployment/host identity. +- A Claude-compatible catalog credential route and owner implementation that + delivers it to the selected runtime. An environment label containing route + references is not credential delivery. +- An owner-defined sandbox profile with explicit enforced provider egress, + DNS/TLS requirements, and negative egress tests. Editing an egress list alone + does not provide connectivity in the current loopback-only implementation. +- Non-secret startup, identity, source-absence, and cleanup evidence through + the same manager/service path the gateway will use. + +Done when these deliverables exist at reviewable revisions and their owner +proofs pass. No rein installation in the source checkout, host home mount, +credential injection from the interactive shell, or unrestricted network +substitution is an acceptance path. + +2026-09-05 owner progress: SAND-WP-0015 implemented digest-pinned standalone +Python runtime mounting and private namespace state. Real rein CLI startup, +read-only runtime, source absence, clean worktree, state persistence across +exec calls, and teardown passed in sandbox d4de9531; sand-boxer lint and 132 +tests pass. This completes the candidate runtime mechanism, not production +selection. Authentication lane, credential delivery/revocation, provider egress, +pinning the Claude executable, and deployed profile review remain open. The +credential catalog has no matching Anthropic/Claude workload lane; the user selected a dedicated Anthropic workload API key. See +[the concrete custody proposal](../docs/anthropic-workload-key.md) and owner +CCR-2026-0016 (in flight). Organization/key metadata is supplied and the user saved the key in OpenBao. +A metadata-only owner check confirmed live version 2 on 2026-09-05, without +reading the value. Custody handoff is complete. Verified machine binding, +protected runtime delivery, workspace scope/budget and provider egress remain +outstanding; provider authentication has not been tested. +See docs/local-profile-acceptance.md. + +## Review and validate the versioned Glas proof candidate + +```task +id: GLAS-WP-0012-T03 +status: wait +depends_on: [GLAS-WP-0012-T02, SECRETS-WP-0009-T03] +priority: high +blocking_reason: "Await the rebuilt runtime artifact carrying profile 1.1.1 started through its owner on railiance01 (SECRETS-WP-0009-T03 step B2)." +state_hub_task_id: "c660808a-1724-555c-bc59-7318870d3591" +``` + +Depends on T02. Prepare `harness.agent-dev-local@1.1.0` only once the real +sandbox profile and credential route are known. Keep the rein/model route and +tool profile explicit; choose bounded proof limits no greater than the current +900 seconds / 60,000 tokens. Record runtime and sandbox revisions and the +validated host scope in evidence. Account for dependency versions that are not +represented directly in the Glas profile schema. + +Use `unverified` only after known blockers are resolved, to label the bounded +real proof. Validate schema, capabilities, route propagation, timeout behavior, +and refusal of the old blocked version. Version coexistence must make an +unversioned selection ambiguous rather than silently changing consumers. + +Done when the exact candidate resolves, the real runtime starts through its +owner, the focused tests and full suite pass, and rollback to the blocked +`harness.agent-dev-local@1.0.0` pin is documented. + +2026-09-21 candidate: `profiles/harness.agent-dev-local-1.1.0.yaml`, readiness +`unverified` (evidence_ref GLAS-WP-0012-T03), `profile.bwrap-local`, no +credential route refs, claude-sonnet-4-6, 900 s / 60,000 tokens, max_turns 30, +max_budget_usd 5.0. The provider key stays with rein-aharness MessagesOwner +outside bwrap (metered-once); the owner bootstrap's sandbox, model and +credential-route checks accept it. max_budget_usd matches the operator-accepted +spend envelope hfact-glas-anthropic-2026-09 (Informed Decision +infd-20260921-b01, approval ba5ce2d8). Unpinned selection is now ambiguous and +1.0.0 stays blocked; rollback is pinning 1.0.0. 134 tests pass. Remaining for +done: the rebuilt runtime artifact carrying this profile starts through its +owner on railiance01 (SECRETS-WP-0009-T03 step B2). + +2026-09-23 the candidate is now `harness.agent-dev-local@1.1.1` (`claude-sonnet-5`, +otherwise identical). 1.1.0 is blocked, and rollback is still pinning 1.0.0. +Reviewed 2026-09-24: the catalog resolves 1.1.1 as unverified, and 134 tests pass. +The done condition is unchanged. It needs runtime `2cfc7b5f` rebuilt with 1.1.1 +and started through its owner. + +## Prove one real bounded rein task inside the sandbox + +```task +id: GLAS-WP-0012-T04 +status: wait +depends_on: [GLAS-WP-0012-T03] +blocking_reason: "Depends on T03." +priority: high +state_hub_task_id: "3e791432-8f37-54bd-a44a-55eeff11ac19" +``` + +Depends on T03. Run the actual `ReinAharness` adapter and installed +`rein-aharness` CLI with Claude through the candidate profile on a disposable +Git fixture. Do not substitute deterministic dispatch or change the source +checkout to satisfy the task. Verify the acceptance assertions in the contract, +including exactly one intended commit, a clean final tree, unchanged/inaccessible +host source, exact identity, bounded duration, task cleanup, and destruction. + +Collect and validate the sandbox artifact before gateway teardown removes the +workspace. Implement the smallest necessary proof observer in the acceptance +fixture; direct evidence must distinguish actual content validation from a +reported commit SHA. Preserve compact value-free evidence only. + +Done when the bounded real run and relevant failure/cleanup checks pass with +the exact selected profile/runtime pins and credential route identified. + +## Publish evidence and mark only the proven profile ready + +```task +id: GLAS-WP-0012-T05 +status: wait +depends_on: [GLAS-WP-0012-T04] +blocking_reason: "Depends on T04." +priority: high +state_hub_task_id: "f7635cf6-e044-52d4-8242-0512e0ccbd7b" +``` + +Depends on T04. Update only the proven candidate to `ready` with evidence that +identifies the host/runtime scope, credential route, egress policy, revisions, +limits, and rollback. Run catalog validation and regression checks. Do not +enable production schedules as a side effect of this proof. + +Update `GLAS-IN-0002` with the Claude acceptance result. Keep its open-weight +portion live, or split that remaining work into a separately registered record +before closing the intake. Synchronize work records and log progress. Finish +this workplan only when the first profile is proven and residuals remain live. + +## Prepare the real-rein acceptance runner + +```task +id: GLAS-WP-0012-T06 +status: done +priority: high +state_hub_task_id: "d0c26709-549f-540a-8250-1fd1b2e4960a" +``` + +Independent preparation for T04 while T02 remains blocked. Add a disposable +fixture that delegates actual dispatch to the catalog-built ReinAharness, +validates the commit and artifact through owner execution before teardown, +checks the host source and cleanup, and emits bounded evidence without model +output or credential values. Refuse blocked or unpinned profiles before +creating a sandbox. Test negative artifact cases and the refusal path. This +runner does not itself satisfy T04 or change readiness. + +Completed 2026-09-05. `scripts/prove-local-profile.py` delegates real dispatch +and observes content/commit acceptance before teardown. It verifies exactly one +new commit, expected paths and committed/worktree content, a clean tree, +source absence, exact child identity, private task mode/removal, and host-source +integrity plus workspace destruction. It emits bounded evidence and has no +readiness override. `tests/test_local_profile_proof.py`: 20 focused tests pass; +full suite: 101 passed; catalog validation passes. A real CLI invocation against +`harness.agent-dev-local@1.0.0` returned `OperationallyBlockedProfileError` +without creating a sandbox. T02–T05 remain waiting on the owner dependencies; +no real model run or readiness promotion is claimed. + +## Acceptance + +One version-pinned local profile runs its actual rein/model task entirely in +the declared sandbox, produces validated local output, and tears down cleanly. +Its readiness describes the proven runtime scope. Other profile readiness and +consumer schedules remain independently governed. + +## 2026-09-05 egress owner return + +Owner-controlled HTTPS egress is implemented and live-tested through both the +extension and a persisted manager: provider TLS/HTTP response, undeclared-host +and direct-IP denial, isolated namespace and proxy/workspace cleanup passed. +See [egress evidence](../docs/evidence/GLAS-WP-0012-egress-2026-09-05.md). +T02 remains waiting on machine auth, protected credential delivery, the pinned +Claude executable and the combined production acceptance. Profiles remain blocked. + +## 2026-09-05 credential owner return + +Sand-boxer credential transport is implemented and proved with synthetic +provider sandbox 880f749e. Owner config binds exact profile/project/actor and +run id; unknown routes refuse, shell API-key fallback is stripped, only the +workload child receives the value, and output redaction precedes truncation. +Tests: sand-boxer171, secrets-engine226, Glas101 pass. Native data-only read +policy/AppRole proposal and activation now live in SECRETS-WP-0009. Its +production exec refuses before OpenBao because durable access-engine decision +records and scoped service authority remain unavailable. T02 remains waiting +on that activation, pinned Claude startup and real provider/task acceptance. + +## 2026-09-06 credential owner update + +Reviewed secrets-engine message `3bd23bc9-e863-4f1f-8c76-415812ec0656`. +Owner reports the local authorization join implemented at `627810b`. Remaining +native activation depends on approval-engine serving the durable +ActionAuthorization claim endpoint and access-engine serving Check, followed +by configuration, per-lane approval and positive/negative verification. +State Hub decisions are not a substitute for the durable authorization object. +SECRETS-WP-0009-T03 and this plan's T02 remain waiting; runtime pinning and +combined real proof remain required. No readiness change or real-key read. + +## 2026-09-06 authorization contract and policy publication + +Reviewed owner commits `7b4b9e3` and `083bee7` and FLEX-WP-0021. Gate-house +GH-DEC-2026-005 resolved the contract: secrets-engine now validates the +approval-claim and decision envelope separately and removed the incorrect +State Hub authority requirement. Flex-auth published +`secrets-engine.catalog-lane.lifecycle` v1; T01/T02 are done. + +Remaining owner gates are real decision/digest verification (FLEX-WP-0021-T03), +consumer service deployment and handoff (T04/T05), approval-engine deployment, +production service identity and verified lane activation. Publication is not +deployment. T02 stays waiting, and runtime pinning and combined acceptance +remain required. No new owner inbox message or activation evidence was found +in this review. + +## 2026-09-06 digest verification follow-up + +Reviewed secrets-engine `6e9c152` and `925d028`: owner corrected correlation +field handling in the flex-auth digest and refreshed fixtures. Its tests also +record an unresolved approval/native action-vocabulary and embedded-claim +digest mismatch for dual-control requests. This is owner integration evidence, +not successful live activation; SECRETS-WP-0009-T03 remains wait. No new +activation receipt was found in the Glas inbox. T02 remains waiting. + +## 2026-09-06 authorization-chain owner return + +Reviewed secrets-engine `f62d3fe` / `64aeec9`. Owner reports claim → Check → +consume → backend integration against a throwaway OpenBao, including denial, +unreachable service, invalid claim, missing digest, consume conflict and action +mismatch refusal before backend access. Authorization transport is stubbed; +wire shapes use published flex-auth fixtures. Do not describe this as a live +production service or Anthropic delivery proof. + +Owner now identifies deployment/configuration as the remaining live gate: +consumer PDP service and approval-engine rollout, production service +credentials, deployed policy pin and per-lane authorization id. +SECRETS-WP-0009-T03 remains wait. Earlier digest-defect notes are historical; +use this latest owner return for the integration status. Native lane positive/ +negative access checks, pinned Claude startup, provider scope/budget inputs and +combined Glas acceptance remain outstanding. T02–T05 stay wait. + +Review found no pending local changes or new inbox messages. Corrected stale +README wording that still called owner execution missing; the implemented +mechanism and its separate proofs do not change profile readiness. + +## 2026-09-06 production deployment action + +User authorized production deployment. Dedicated flex-auth-secrets-engine Helm +release is available 1/1 at revision 1; five policy fixtures and allowed/denied +network probes passed. CI image is digest-pinned; existing consumers unchanged. +Caller authentication remains warn pending identity adoption. See +[deployment evidence](../history/2026-09-06-174000-production-deployment.md). +Approval-engine remains undeployed: live KeyCape clients and audit sender are +absent, the tenant contract needs alignment, and a release digest is required. +Corrected its stale issuer/JWKS deployment inputs. No production credential +lane or real Claude run was activated; T02 remains wait. + +## Production dependency coordination + +GLAS-WP-0015 now tracks exact owner handoffs and return receipts from Glas. +The requests in docs/production-dependency-handoffs.md reuse existing owner +work: tenant agreement and client custody, KeyCape rollout, audit source +onboarding, approval release, native lane activation and pinned combined +runtime. Image/runtime preparation can proceed in parallel. The runner's +project glas-local-proof must be reconciled with the owner route example's +project glas-harness before the exact delivery binding is selected. Owner +handoffs were sent after explicit authorization; GLAS-WP-0015 records all seven +receipt IDs and tracks replies. This plan's T02 remains wait. + +## Verified owner follow-up — 2026-09-06 + +Glas reviewed six owner acknowledgements in GLAS-WP-0015. Project binding is +resolved to agt/glas-local-proof at sand-boxer 23d0c2b; the combined profile +source is available, but Claude is not pinned. The deployed policy was found +to lack tenant denial at v1; the owner correction v2 is now deployed at Helm +revision 2 and six live Check fixtures pass, including wrong_tenant denial. +Tenant choice, workstation caller access, audit load-bearing schema/admission, +identity/custody and native activation remain gates. T02 remains wait. + + +Tenant decision update (2026-09-06): operator approved `tenant:platform` for the +approval dependency chain as the platform management/services tenant. See +`docs/platform-tenant-decision.md`. The choice blocker is resolved; exact owner +configuration alignment and verification remain part of T02 before readiness. + + +Later deployment progress (2026-09-06): KeyCape/approval source tenant alignment +verified, approval release published and pinned, audit T03 complete, dedicated +PDP revision 3 enforces adopted short-lived caller identity. See GLAS-WP-0015 +and its evidence receipts. T02 remains wait until remaining owner admission and +runtime gates are verified; no real model run or readiness promotion. + + +## Pinned Claude startup progress — 2026-09-06 + +Advanced sand-boxer SAND-WP-0015-T05 independently of custody: runtime builder +now copies only an explicitly digest-pinned native Claude executable. Selected +installed Claude 2.1.263, binary digest +26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba; +complete candidate runtime digest +5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922. +Sandbox f8821ec2 passed actual Claude --version, rein CLI/import, read-only +runtime, private HOME/state persistence, source absence, clean tree and teardown +with owner proxy environment present. Lint and all 175 sand-boxer tests pass. +Evidence: docs/evidence/GLAS-WP-0015-claude-startup-2026-09-06.json. + +This resolves candidate binary pin/startup, not protected runtime deployment, +Claude provider-request compatibility, credential delivery or a real model run. +The candidate is in /tmp; no production profile readiness changed. Existing +credential and acceptance tasks remain open. No new owner replies at session start. + + +## Protected local artifact return — 2026-09-08 + +SAND-WP-0015-T06 is done. The previously pinned combined artifact is installed +unchanged in owner UID 1000's mode-0700 runtime store on bnt-lap001: +`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`. +Complete artifact digest remains +`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`. +This is the existing owner-controlled local artifact contract, not root-owned +system storage or Railiance placement. Do not rebuild this accepted candidate +merely to move it out of /tmp. + +The installed-path sandbox `51b59587` proves actual Claude 2.1.263 and rein +startup, read-only runtime, source absence, clean Git, private state persistence, +loopback-only interfaces, and workspace/proxy removal. No credential references +or model request were used. Sand-boxer `make check`: lint clean, 188 tests pass. +Source return: 3e49a98a0e2c4a64539a5ccd674be8cd67ac9845; receipt in +`sand-boxer/docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`. + +T02 remains wait for trusted owner execution configuration, native credentials +and real-provider evidence. T03-T06 retain candidate review, real-model run, +readiness and recovery obligations. No profile readiness or schedule changes. + +2026-09-23 candidate superseded before use: operator chose `claude-sonnet-5`. +`harness.agent-dev-local@1.1.1` is identical to 1.1.0 except the model; 1.1.0 is +now `blocked`. Runtime `2cfc7b5f` bundles the earlier 1.1.0 copy (profile sha +`e84b9d0f…`) and must be rebuilt with this revision and placed again +(SECRETS-WP-0009-T03). Claude CLI 2.1.266 in that runtime lists +`claude-sonnet-5` in its model registry (static check). 134 tests pass. + +## 2026-09-27 loose-ends review + +Reviewed for locally finishable work. No new owner reply advances T02 beyond +the 2026-09-23 secrets-engine status already recorded in GLAS-WP-0015-T03. +T03's remaining done condition (runtime `2cfc7b5f` rebuilt with 1.1.1 and +started through its owner on railiance01, SECRETS-WP-0009-T03 step B2) and +T04/T05 all require that external owner delivery. Nothing in this repo can +advance them today; `status: blocked` stands unchanged. Re-review when the +runtime rebuild/placement or owner authentication lands. + + +## EMERGENT-WP-0002 proof requirements — 2026-10-03 + +Scoped review and local observed-model correction: +`docs/evidence/2026-10-03-emergent-session-readiness-review.md`. +T04 retains actual native-provider startup and continuity acceptance, including +reload after real resume/compaction, provider/repository changes and exact +available model/session metadata. The current lifecycle exposes no continuity +event API. Existing owner-delivery blockers and all task/readiness states remain; +local deterministic tests are not governed provider or isolation acceptance. + + +## 2026-10-16 NetKingdom isolated integration preparation + +Prepared, without owner deployment or readiness promotion, a Glas change that +separates credential-free housekeeping commands from explicit rein workload +execution. Both concrete adapters use the workload path; SSH selections with +credential routes refuse because that transport has no owner delivery contract. +Full regression: 141 passed. Real disposable bwrap/Glas transport proof passes +synthetic host-file denial, inherited credential environment and subprocess +denial, workload-only delivery, redaction, subsequent-command absence and +teardown. No provider/model call or real credential read occurred. + +Latest owner dependency sources supersede the stale missing-lane account: +September 27 native lanes were activated and revoked; corrected immutable +owner runtime is `818b1b7c…` (October 15 startup proof), not yet installed or +admitted on Railiance. HARNESS-WP-0008-T07 retains memo lifecycle acceptance, +installation, rehearsal, renewal/pin updates, native approvals and bounded +successful model/tool/commit acceptance. Any subsequent build containing this +patch has a different source/artifact pin requiring owner admission. + +This is a reviewable patch in Custodian's NetKingdom change bundle; no canonical +source synchronization or workplan task completion is implied. CUST-WP-0073 +continues to require admission of the actual interactive assignment, including +its existing host credential and external tool paths, not a disposable fixture. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/sand-boxer-current/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/sand-boxer-current/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md new file mode 100644 index 00000000..34c6aeba --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/sand-boxer-current/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md @@ -0,0 +1,375 @@ +--- +id: SAND-WP-0015 +type: workplan +title: "Provide a pinned bwrap rein runtime and private state" +domain: infotech +repo: sand-boxer +status: blocked +flavor: implementation +owner: codex +topic_slug: bwrap-runtime-and-private-state +created: "2026-09-05" +updated: "2026-10-04" +state_hub_workstream_id: "d3f12387-fd23-58f0-b979-9c811507614d" +--- + +# Provide a pinned bwrap rein runtime and private state + +Implement the runtime prerequisites returned by Glas in `GLAS-WP-0012` and +`docs/local-profile-acceptance.md`. Continue owner work `SAND-WP-0014-T05` and +live residual `GLAS-IN-0002`. Runtime startup is a separate acceptance gate +from credential delivery, enforced provider egress, and a real model task. + +## Mount an owner-selected, digest-pinned Python runtime + +```task +id: SAND-WP-0015-T01 +status: done +priority: high +state_hub_task_id: "93e4aad8-ba9b-5850-93d4-b5bf78ac8978" +``` + +Add exact artifact verification to trusted extension configuration and mount +the standalone runtime read-only at a fixed namespace path. Reject altered +content/modes, unexpected files, escaping symlinks, special files, and source +or workspace overlap. A caller cannot choose runtime paths through exec input. +Provide a non-editable rein-aharness/llm-connect bundle builder with recorded +source revisions and resolved dependency versions. + +Completed 2026-09-05. `extensions/runtime.py` verifies complete artifact +contents/modes before a read-only mount; the builder installed committed +rein-aharness `1429db5` and llm-connect `0056094` without editable source paths. +Candidate digest and source/package evidence are in `docs/bwrap-runtime.md`. + +## Keep writable runtime state outside the repository + +```task +id: SAND-WP-0015-T02 +status: done +priority: high +state_hub_task_id: "c8a689f8-bdf2-57a2-8521-a1c0ca44e750" +``` + +Create private mode-0700 HOME/config/cache/state/tmp directories within the +namespace, preserve them across exec requests, and remove them with teardown. +Keep the command environment sanitized and disable Python user-site/bytecode +writes. Fail closed on profile network/setup-credential declarations that the +current bwrap implementation cannot honor. + +Completed 2026-09-05. Mode-0700 namespace HOME and XDG/TMP directories sit +outside the copied Git tree. Explicit runtime PATH selection remains owner +controlled. Unsupported egress/default-allow and setup credentials now refuse +before workspace creation; regression tests cover these boundaries. + +## Verify the real rein runtime and owner regression paths + +```task +id: SAND-WP-0015-T03 +status: done +priority: high +state_hub_task_id: "1a3002f6-c6a8-59d0-9d38-942028419de5" +``` + +Run `make check`, the existing authenticated cross-request owner smoke, and a +real runtime startup smoke. Verify the actual rein CLI and adapter imports, +read-only runtime, private HOME, clean worktree, absent source, loopback-only +network, and teardown. Keep the production Glas profile blocked. + +Completed 2026-09-05. `make check`: lint clean, 132 tests passed. Authenticated +owner API smoke `223db65b` returned HTTP 200 and proved exact consumer identity, +stdin delivery, absent source, loopback-only network and complete teardown. +Pinned runtime smoke `d4de9531` ran the actual rein CLI and imported its Claude +adapter, proved read-only runtime/private HOME/clean worktree, retained private +state across a second exec, and removed the workspace. No model call or +credential acquisition occurred; T04 remains waiting. + +## Resolve Claude credentials, enforced egress, and production acceptance + +```task +id: SAND-WP-0015-T04 +status: wait +blocked_on: message-from:rein-aharness +blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt." +priority: high +state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93" +``` + +The 2026-09-05 routing lookup found no concrete Anthropic/Claude workload lane. +The generic OpenBao template is not a delegable lane; the OpenRouter AppRole +belongs to another rein. Credential owner railiance-platform/OpenBao plus +rein-aharness must establish a concrete Claude-compatible route and delivery/ +revocation contract before values can be requested or supplied to the runtime. + +Sand-boxer must then implement the matching enforced provider egress contract +(including DNS/TLS and denied-destination proof) and pin/deploy the actual +Claude executable. No unrestricted-network or interactive-login substitute is +permitted. Review the deployed runtime/profile with Glas, run its real-rein +acceptance fixture, and update only the proven profile's readiness. No +production schedule is enabled by the runtime-startup smoke. + +This task and SAND-WP-0014-T05 remain open until those gates pass. The detailed +return contract and runtime proof are in `docs/bwrap-runtime.md`; the live +cross-repo residual remains `GLAS-IN-0002`. + +## 2026-09-05 transport implementation evidence + +T04 remains waiting on owner machine authentication, protected credential +delivery, pinned Claude startup and the real model proof. CCR-2026-0016 custody +is confirmed by a metadata-only check of live version 2; no value was read. +HTTPS egress is now implemented as an exact owner/profile allowlisted CONNECT +proxy while retaining an isolated network namespace. Non-secret live smoke +sandbox e290e788 verified provider TLS response, undeclared destination and +direct-IP denial, and proxy/workspace teardown. See docs/bwrap-egress.md. + +## 2026-09-05 credential transport owner return + +Owner-bound exec-env transport is implemented and proved with a synthetic +provider in real sandbox 0e5fb35a. Exact route/profile/project/actor/run binding, +child-only key injection, pre-truncation redaction, next-exec absence and teardown +pass. No real key read or production profile activation. See docs/bwrap-credentials.md. +SECRETS-WP-0009 now owns native AppRole adoption for CCR-2026-0016, including +a data-only consumer read policy. Its production exec refuses before OpenBao +until SECRETS-WP-0007-T04 and SECRETS-WP-0008-T02/T06 deliver canonical +authorization/consume and service authority. T04 remains waiting on that live +owner path, the pinned Claude executable and real model acceptance. + +## 2026-09-06 combined-runtime candidate returned to Glas + +Answered the GLAS-WP-0015 handoff. Added candidate profile +`profile.claude-agent-dev-proof` version 1.1.0 (`ext.bwrap`, localhost-only, +`default: deny` with declared `api.anthropic.com:443`) for GLAS-WP-0012 review. +The committed profile grants no egress by itself: `ext.bwrap` refuses it unless +the owner extension config allowlists the destination, and a regression test now +asserts that fail-closed default. `make check`: lint clean, 172 tests passed. + +Resolved the differing project examples in favour of the acceptance runner's +`actor: agt` / `project: glas-local-proof`, and bound the documented credential +route to that single project rather than broadening it. The full return contract +— profile id/revision, host scope, consumer tuple, runtime digest and mount +paths, declared egress, and the value-free denial/cleanup receipts — is in +`docs/bwrap-runtime.md`. + +T04 stays `wait`: the concrete Claude credential lane and delivery/revocation +contract, owner machine authentication, the pinned Claude executable with its +own proxy startup proof, and the real-model acceptance are still operator and +upstream-owner gated. Provider workspace scope and spend limit remain operator +inputs. `SAND-WP-0014-T05` remains open on the same gates. + + +## Package and prove the pinned Claude executable independently of custody + +```task +id: SAND-WP-0015-T05 +status: done +priority: high +state_hub_task_id: "f0163cda-b581-56a0-a0dd-9c8a436f5bbe" +``` + +Split the independent executable packaging/startup portion from T04 so credential +custody does not hide completed local runtime work. Implement explicit binary +pinning, reject mismatches/host wrappers, and prove actual startup with the +broker proxy environment and complete cleanup. T04 retains protected deployment, +credential adoption and real-provider acceptance. + + +## Pinned Claude candidate and isolated startup — 2026-09-06 + +The builder now accepts `--claude-binary`, `--claude-sha256` and +`--claude-version` together. It requires a regular native ELF file, verifies the +exact copied bytes, refuses symlinks/wrappers/mismatches and existing destination +files, and copies only the executable as bin/claude mode 0755. The expected +version is metadata until the sandbox smoke checks the actual output. No +interactive HOME, provider key or login state is copied. + +Candidate provenance: the explicitly selected installed native release +`/home/worsch/.local/share/claude/versions/2.1.263`, SHA-256 +`26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba`. +This is a digest pin of the selected local artifact, not an upstream signature +verification claim. Complete runtime candidate: +`/tmp/sandboxer-claude-runtime-20260906`, SHA-256 +`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`. +Mount remains `/opt/sandboxer/runtime`; Claude is `/opt/sandboxer/runtime/bin/claude`. +Rein sources remain 1429db5 / llm-connect 0056094; resolved dependency versions +are recorded in the evidence. Rebuilding may resolve different dependencies; +the complete digest pins this artifact, not future builds. + +Sandbox f8821ec2 ran real `claude --version` -> `2.1.263 (Claude Code)` plus rein +CLI/import checks, with owner allowlisted provider egress and HTTPS_PROXY set +by the broker. Runtime read-only, private 0700 HOME/state persistence, clean Git, +absent source and loopback-only interfaces passed; workspace and proxy removed. +`make check`: lint passed, 175 tests passed. + +Evidence: docs/evidence/SAND-WP-0015-claude-startup-2026-09-06.json. +Reproduce with the builder options above and `scripts/smoke-bwrap-runtime.py +--runtime-path --runtime-sha256 --claude-version 2.1.263`. + +Limits: `--version` proves startup with the proxy environment, not that Claude +has sent a provider request through it. No credential acquisition or model run. +The /tmp candidate is not production placement; protected artifact installation, +owner configuration, credential adoption and real-model proof remain T04 gates. + + +## Install the pinned runtime in the local owner's protected store + +```task +id: SAND-WP-0015-T06 +status: done +priority: high +assignee: the-custodian +state_hub_task_id: "f85f54c9-1081-5d0c-95ad-aca20ede8d66" +``` + +HFACT-WP-0001-T04 consumes this independent installation return. Reuse the +already verified combined candidate with digest +`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`; +do not rebuild resolved dependencies. Publish it under the local owner's +private artifact store, verify exact copied content/modes and refusal of unsafe +or corrupted destinations, and prove startup/read-only access/teardown through +the installed path. The owner remains trusted; this is not root-owned storage. +T04 retains real credential adoption, owner configuration, model acceptance +and production placement. No credential route or profile is activated here. + +`scripts/install-bwrap-runtime.py` installs below the owner's home into a +mode-0700 store, verifies before and after copying, preserves artifact modes +and internal symlinks, serializes publishers and never overwrites a published +artifact. Existing modified artifacts are refused rather than repaired silently. +Group/other-writable ancestors, unsafe permission bits and store aliases refuse. +Unit tests cover integrity, idempotence, copy-time corruption and access modes. + + +T06 completed 2026-09-08 on bnt-lap001, local owner UID 1000. Installed the +unchanged 358-entry / 245176062-byte candidate at +`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`. +The private store is mode 0700; the installing owner remains trusted. A root-owned +system installation was not claimed or required for this local owner contract. +The installed-path sandbox smoke `51b59587` passed real Claude 2.1.263 and rein +startup, read-only mount, private state persistence, clean worktree, absent source, +loopback-only interfaces and complete workspace/proxy teardown. No model call +or credential acquisition occurred. `make check`: lint clean, 188 tests passed. +Two existing tests require the checkout directory name `sand-boxer`; the final +full check used that canonical basename. Evidence: +`docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`. +T04 retains owner execution configuration, native credential/egress and real-model +acceptance; Railiance installation needs its own target-specific return. + +## 2026-09-09 factory metered route source return + +Implemented the trusted, ephemeral Messages route described in +[docs/bwrap-messages-route.md](../docs/bwrap-messages-route.md). Actual local +bwrap owner transport proves provider key/ledger separation, direct-route denial, +revocation and teardown; rein also proves metered request plus commit import and +close replay. `make check`: lint clean, 199 passed. T04 remains waiting for the +admitted provider-to-owner bootstrap, updated protected runtime/profile, Railiance +placement, live compatibility and G0. Existing child-provider-key/direct-CONNECT +proofs do not admit this different credential holder or metered profile. No CCR, +secret read, deployment or paid request was performed. + +## 2026-09-09 standalone owner packaging and runtime selection + +Added frozen-lock owner build mode for the matched rein/llm/Glas/sandboxer set, +with non-editable installation and recorded lock/source/package pins. Sand-boxer +wheels now carry their profile and extension definitions, fixing a bwrap owner +failure that source-checkout tests could hide. The trusted Messages binding can +select the digest-pinned runtime without adding an API or profile override. +`make check`: lint clean, 205 passed. Existing standalone workload builds remain +supported. See docs/bwrap-runtime.md and rein's docs/owner-bootstrap.md. + +The project's `evidence/2026-09-09-owner-bootstrap.json` records the actual candidate +build and isolated installed-interpreter/CLI proof. T04 remains wait for accepted +credential-to-owner delivery, current protected installation and Railiance placement, +provider compatibility and G0. Source packaging does not reopen completed T01-T03, +T05-T06, broaden the old CCRs or activate the previously installed 2.1.263 artifact. + +The first standalone candidate exposed a stale cached local wheel despite current +Git metadata. Owner builds now refresh all local distributions and compare every +installed package source/definition file with its tracked source; stale, missing or +extra files refuse the build. Four content-conformance regressions cover that gap. + + +## 2026-09-27 installed launcher defect and guarded correction + +REINAH-WP-0003's single admitted attempt completed native provider/companion +apply, verify and exec, including scoped-token revocation. The claimed job +`6efa9436-6a91-47c0-94e5-b31b5a0e7e3a` failed before any provider request +reservation. The sandbox was destroyed and the source repository is unchanged. +The parent EUR 10 liability remains held for explicit reconciliation; no retry +or replacement authority is inferred. + +Installed artifact `b6e4e8a4` contains uv-generated three-line shell launchers +that still reference a vanished temporary build interpreter. The builder had +only rewritten direct Python shebangs. Fixed both generated forms and added +actual relocation/execution tests for direct, long and space-containing build +paths, plus refusal of unknown governed launchers. Builder tests: 12 passed; +focused lint passes. Rein's real installed bwrap probe reproduces both broken +entrypoints at exit 127 with zero provider forwards; its new bootstrap checks +refuse the malformed artifact before claiming. + +T04 remains `wait` and this plan stays `blocked`: replacement artifact build/ +admission and a separately authorized successful model/tool/commit proof remain. +The existing SAND-WP-0015 / REINAH-WP-0003 / SECRETS-WP-0009 records retain the +work. No new task or workplan. Detailed non-secret receipts are in rein-aharness +`docs/evidence/2026-09-27-metered-*.json`. + + +## 2026-10-04 offline owner startup proof preparation + +The existing no-credential smoke command now supports +`--require-owner-entrypoints`: the admitted digest-pinned runtime must start both +`rein-aharness --help` and `glas-harness --help`, and imported rein, llm-connect, +Glas and sandboxer modules must originate inside the mounted artifact. Broken or +missing launchers, failed help, timeout and external package origins cannot be +recorded as startup success. The default standalone mode remains supported. +Launcher diagnostics are not copied into the receipt. Focused tests exercise +real subprocesses, including the vanished-build-interpreter failure. + +This is preparation for a corrected immutable candidate's independent native +startup proof; it is not that candidate's receipt or owner acceptance. T04 stays +`wait`, the plan stays `blocked`, and the Sept27 artifact/liability evidence +remains intact. Replacement artifact admission and pins, explicit retained +EUR 10 reconciliation, and a separately authorized successful model/tool/commit +attempt remain with the existing owners. No credentials, provider request, +queue claim, policy activation or retry authorization is supplied here. + +2026-10-04 current blocker reconciliation: September 27 native provider/companion +apply/verify/exec and successful revocation supersede the original missing +platform route. The remaining owner return is rein-aharness corrected artifact/ +profile admission, retained EUR10 reconciliation and fresh retry authority. +T04 stays wait; no renewed trial is authorized. Full owner check: lint clean, +219 tests passed (one existing Starlette deprecation warning). + +## 2026-10-13 owner returns: reconciliation and corrected build + +rein-aharness `d897a83` records owner reconciliation of the Sept 27 held +liability: run `6efa9436` failed at launcher exit 127 before any provider +reservation; liability stays at the full hold, exhausting that one-attempt +envelope, and the owner authorized a new retry envelope on 2026-10-13. +rein-aharness `9a4469d` records corrected runtime `ee3d0a53…` (replaces +`b6e4e8a4…`; only rein moved, adding pre-claim launcher rejection), built with +the sand-boxer builder fix; both launchers exit 0 inside bwrap. Receipts: +rein-aharness `docs/evidence/2026-10-13-*.json`. + +Remaining before T04 closes: install `ee3d0a53` on railiance01, pin its digest +in owner config, pass `--require-owner-entrypoints` offline startup proof, then +run the single authorized attempt to a successful model/tool/commit receipt. +T04 stays `wait`; the plan stays `blocked`. + +2026-10-14 check: rein-aharness pushed `9a4469d` (corrected build record) and +opened REINAH-WP-0008 (simpler metered retry, active). No railiance01 install/ +admission of `ee3d0a53` or successful acceptance receipt yet; request sent on +hub message `82faf25c`. T04 stays `wait`; plan stays `blocked`. + +2026-10-14 rein-aharness reply (thread `82faf25c`): step 1 done (`9a4469d` on +origin/main). Steps 2-4 are REINAH-WP-0008-T07. Budget is now up to three +attempts within EUR 10 under memo `infd-20261014-b01` (observed-usage +settlement); order: memo acceptance in the attended informed-decision sitting, +rebuild and install `ee3d0a53` on railiance01, `metered-once --rehearse`, +`owner renew`, first attempt. Railiance01 still pins `b6e4e8a4`. T04 stays `wait`. + +2026-10-15 owner-approved rebuild: the pinned inputs of the `ee3d0a53` receipt +(same four commits, uv.lock `8775904b…`, Claude 2.1.266 `19842705…`) rebuilt +to `818b1b7c8292…f359`. The digest differs only because the builder embeds +absolute source paths (`direct_url.json`, `build-info.json`). Owner-entrypoint +startup smoke passed in sandbox `28df7a1d`; no credentials or model calls. +Sent to rein-aharness (message `8a7962f7`, thread `82faf25c`) to record as the +then-current corrected runtime for REINAH-WP-0008-T07 and to load the unsigned +memo sitting. Nothing installed on railiance01. T04 stays `wait`. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/sand-boxer/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/sand-boxer/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md new file mode 100644 index 00000000..9798568e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/driver-proposals/sand-boxer/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md @@ -0,0 +1,375 @@ +--- +id: SAND-WP-0015 +type: workplan +title: "Provide a pinned bwrap rein runtime and private state" +domain: infotech +repo: sand-boxer +status: blocked +flavor: implementation +owner: codex +topic_slug: bwrap-runtime-and-private-state +created: "2026-09-05" +updated: "2026-10-04" +state_hub_workstream_id: "d3f12387-fd23-58f0-b979-9c811507614d" +--- + +# Provide a pinned bwrap rein runtime and private state + +Implement the runtime prerequisites returned by Glas in `GLAS-WP-0012` and +`docs/local-profile-acceptance.md`. Continue owner work `SAND-WP-0014-T05` and +live residual `GLAS-IN-0002`. Runtime startup is a separate acceptance gate +from credential delivery, enforced provider egress, and a real model task. + +## Mount an owner-selected, digest-pinned Python runtime + +```task +id: SAND-WP-0015-T01 +status: done +priority: high +state_hub_task_id: "93e4aad8-ba9b-5850-93d4-b5bf78ac8978" +``` + +Add exact artifact verification to trusted extension configuration and mount +the standalone runtime read-only at a fixed namespace path. Reject altered +content/modes, unexpected files, escaping symlinks, special files, and source +or workspace overlap. A caller cannot choose runtime paths through exec input. +Provide a non-editable rein-aharness/llm-connect bundle builder with recorded +source revisions and resolved dependency versions. + +Completed 2026-09-05. `extensions/runtime.py` verifies complete artifact +contents/modes before a read-only mount; the builder installed committed +rein-aharness `1429db5` and llm-connect `0056094` without editable source paths. +Candidate digest and source/package evidence are in `docs/bwrap-runtime.md`. + +## Keep writable runtime state outside the repository + +```task +id: SAND-WP-0015-T02 +status: done +priority: high +state_hub_task_id: "c8a689f8-bdf2-57a2-8521-a1c0ca44e750" +``` + +Create private mode-0700 HOME/config/cache/state/tmp directories within the +namespace, preserve them across exec requests, and remove them with teardown. +Keep the command environment sanitized and disable Python user-site/bytecode +writes. Fail closed on profile network/setup-credential declarations that the +current bwrap implementation cannot honor. + +Completed 2026-09-05. Mode-0700 namespace HOME and XDG/TMP directories sit +outside the copied Git tree. Explicit runtime PATH selection remains owner +controlled. Unsupported egress/default-allow and setup credentials now refuse +before workspace creation; regression tests cover these boundaries. + +## Verify the real rein runtime and owner regression paths + +```task +id: SAND-WP-0015-T03 +status: done +priority: high +state_hub_task_id: "1a3002f6-c6a8-59d0-9d38-942028419de5" +``` + +Run `make check`, the existing authenticated cross-request owner smoke, and a +real runtime startup smoke. Verify the actual rein CLI and adapter imports, +read-only runtime, private HOME, clean worktree, absent source, loopback-only +network, and teardown. Keep the production Glas profile blocked. + +Completed 2026-09-05. `make check`: lint clean, 132 tests passed. Authenticated +owner API smoke `223db65b` returned HTTP 200 and proved exact consumer identity, +stdin delivery, absent source, loopback-only network and complete teardown. +Pinned runtime smoke `d4de9531` ran the actual rein CLI and imported its Claude +adapter, proved read-only runtime/private HOME/clean worktree, retained private +state across a second exec, and removed the workspace. No model call or +credential acquisition occurred; T04 remains waiting. + +## Resolve Claude credentials, enforced egress, and production acceptance + +```task +id: SAND-WP-0015-T04 +status: wait +blocked_on: message-from:rein-aharness +blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt." +priority: high +state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93" +``` + +The 2026-09-05 routing lookup found no concrete Anthropic/Claude workload lane. +The generic OpenBao template is not a delegable lane; the OpenRouter AppRole +belongs to another rein. Credential owner railiance-platform/OpenBao plus +rein-aharness must establish a concrete Claude-compatible route and delivery/ +revocation contract before values can be requested or supplied to the runtime. + +Sand-boxer must then implement the matching enforced provider egress contract +(including DNS/TLS and denied-destination proof) and pin/deploy the actual +Claude executable. No unrestricted-network or interactive-login substitute is +permitted. Review the deployed runtime/profile with Glas, run its real-rein +acceptance fixture, and update only the proven profile's readiness. No +production schedule is enabled by the runtime-startup smoke. + +This task and SAND-WP-0014-T05 remain open until those gates pass. The detailed +return contract and runtime proof are in `docs/bwrap-runtime.md`; the live +cross-repo residual remains `GLAS-IN-0002`. + +## 2026-09-05 transport implementation evidence + +T04 remains waiting on owner machine authentication, protected credential +delivery, pinned Claude startup and the real model proof. CCR-2026-0016 custody +is confirmed by a metadata-only check of live version 2; no value was read. +HTTPS egress is now implemented as an exact owner/profile allowlisted CONNECT +proxy while retaining an isolated network namespace. Non-secret live smoke +sandbox e290e788 verified provider TLS response, undeclared destination and +direct-IP denial, and proxy/workspace teardown. See docs/bwrap-egress.md. + +## 2026-09-05 credential transport owner return + +Owner-bound exec-env transport is implemented and proved with a synthetic +provider in real sandbox 0e5fb35a. Exact route/profile/project/actor/run binding, +child-only key injection, pre-truncation redaction, next-exec absence and teardown +pass. No real key read or production profile activation. See docs/bwrap-credentials.md. +SECRETS-WP-0009 now owns native AppRole adoption for CCR-2026-0016, including +a data-only consumer read policy. Its production exec refuses before OpenBao +until SECRETS-WP-0007-T04 and SECRETS-WP-0008-T02/T06 deliver canonical +authorization/consume and service authority. T04 remains waiting on that live +owner path, the pinned Claude executable and real model acceptance. + +## 2026-09-06 combined-runtime candidate returned to Glas + +Answered the GLAS-WP-0015 handoff. Added candidate profile +`profile.claude-agent-dev-proof` version 1.1.0 (`ext.bwrap`, localhost-only, +`default: deny` with declared `api.anthropic.com:443`) for GLAS-WP-0012 review. +The committed profile grants no egress by itself: `ext.bwrap` refuses it unless +the owner extension config allowlists the destination, and a regression test now +asserts that fail-closed default. `make check`: lint clean, 172 tests passed. + +Resolved the differing project examples in favour of the acceptance runner's +`actor: agt` / `project: glas-local-proof`, and bound the documented credential +route to that single project rather than broadening it. The full return contract +— profile id/revision, host scope, consumer tuple, runtime digest and mount +paths, declared egress, and the value-free denial/cleanup receipts — is in +`docs/bwrap-runtime.md`. + +T04 stays `wait`: the concrete Claude credential lane and delivery/revocation +contract, owner machine authentication, the pinned Claude executable with its +own proxy startup proof, and the real-model acceptance are still operator and +upstream-owner gated. Provider workspace scope and spend limit remain operator +inputs. `SAND-WP-0014-T05` remains open on the same gates. + + +## Package and prove the pinned Claude executable independently of custody + +```task +id: SAND-WP-0015-T05 +status: done +priority: high +state_hub_task_id: "f0163cda-b581-56a0-a0dd-9c8a436f5bbe" +``` + +Split the independent executable packaging/startup portion from T04 so credential +custody does not hide completed local runtime work. Implement explicit binary +pinning, reject mismatches/host wrappers, and prove actual startup with the +broker proxy environment and complete cleanup. T04 retains protected deployment, +credential adoption and real-provider acceptance. + + +## Pinned Claude candidate and isolated startup — 2026-09-06 + +The builder now accepts `--claude-binary`, `--claude-sha256` and +`--claude-version` together. It requires a regular native ELF file, verifies the +exact copied bytes, refuses symlinks/wrappers/mismatches and existing destination +files, and copies only the executable as bin/claude mode 0755. The expected +version is metadata until the sandbox smoke checks the actual output. No +interactive HOME, provider key or login state is copied. + +Candidate provenance: the explicitly selected installed native release +`/home/worsch/.local/share/claude/versions/2.1.263`, SHA-256 +`26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba`. +This is a digest pin of the selected local artifact, not an upstream signature +verification claim. Complete runtime candidate: +`/tmp/sandboxer-claude-runtime-20260906`, SHA-256 +`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`. +Mount remains `/opt/sandboxer/runtime`; Claude is `/opt/sandboxer/runtime/bin/claude`. +Rein sources remain 1429db5 / llm-connect 0056094; resolved dependency versions +are recorded in the evidence. Rebuilding may resolve different dependencies; +the complete digest pins this artifact, not future builds. + +Sandbox f8821ec2 ran real `claude --version` -> `2.1.263 (Claude Code)` plus rein +CLI/import checks, with owner allowlisted provider egress and HTTPS_PROXY set +by the broker. Runtime read-only, private 0700 HOME/state persistence, clean Git, +absent source and loopback-only interfaces passed; workspace and proxy removed. +`make check`: lint passed, 175 tests passed. + +Evidence: docs/evidence/SAND-WP-0015-claude-startup-2026-09-06.json. +Reproduce with the builder options above and `scripts/smoke-bwrap-runtime.py +--runtime-path --runtime-sha256 --claude-version 2.1.263`. + +Limits: `--version` proves startup with the proxy environment, not that Claude +has sent a provider request through it. No credential acquisition or model run. +The /tmp candidate is not production placement; protected artifact installation, +owner configuration, credential adoption and real-model proof remain T04 gates. + + +## Install the pinned runtime in the local owner's protected store + +```task +id: SAND-WP-0015-T06 +status: done +priority: high +assignee: the-custodian +state_hub_task_id: "f85f54c9-1081-5d0c-95ad-aca20ede8d66" +``` + +HFACT-WP-0001-T04 consumes this independent installation return. Reuse the +already verified combined candidate with digest +`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`; +do not rebuild resolved dependencies. Publish it under the local owner's +private artifact store, verify exact copied content/modes and refusal of unsafe +or corrupted destinations, and prove startup/read-only access/teardown through +the installed path. The owner remains trusted; this is not root-owned storage. +T04 retains real credential adoption, owner configuration, model acceptance +and production placement. No credential route or profile is activated here. + +`scripts/install-bwrap-runtime.py` installs below the owner's home into a +mode-0700 store, verifies before and after copying, preserves artifact modes +and internal symlinks, serializes publishers and never overwrites a published +artifact. Existing modified artifacts are refused rather than repaired silently. +Group/other-writable ancestors, unsafe permission bits and store aliases refuse. +Unit tests cover integrity, idempotence, copy-time corruption and access modes. + + +T06 completed 2026-09-08 on bnt-lap001, local owner UID 1000. Installed the +unchanged 358-entry / 245176062-byte candidate at +`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`. +The private store is mode 0700; the installing owner remains trusted. A root-owned +system installation was not claimed or required for this local owner contract. +The installed-path sandbox smoke `51b59587` passed real Claude 2.1.263 and rein +startup, read-only mount, private state persistence, clean worktree, absent source, +loopback-only interfaces and complete workspace/proxy teardown. No model call +or credential acquisition occurred. `make check`: lint clean, 188 tests passed. +Two existing tests require the checkout directory name `sand-boxer`; the final +full check used that canonical basename. Evidence: +`docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`. +T04 retains owner execution configuration, native credential/egress and real-model +acceptance; Railiance installation needs its own target-specific return. + +## 2026-09-09 factory metered route source return + +Implemented the trusted, ephemeral Messages route described in +[docs/bwrap-messages-route.md](../docs/bwrap-messages-route.md). Actual local +bwrap owner transport proves provider key/ledger separation, direct-route denial, +revocation and teardown; rein also proves metered request plus commit import and +close replay. `make check`: lint clean, 199 passed. T04 remains waiting for the +admitted provider-to-owner bootstrap, updated protected runtime/profile, Railiance +placement, live compatibility and G0. Existing child-provider-key/direct-CONNECT +proofs do not admit this different credential holder or metered profile. No CCR, +secret read, deployment or paid request was performed. + +## 2026-09-09 standalone owner packaging and runtime selection + +Added frozen-lock owner build mode for the matched rein/llm/Glas/sandboxer set, +with non-editable installation and recorded lock/source/package pins. Sand-boxer +wheels now carry their profile and extension definitions, fixing a bwrap owner +failure that source-checkout tests could hide. The trusted Messages binding can +select the digest-pinned runtime without adding an API or profile override. +`make check`: lint clean, 205 passed. Existing standalone workload builds remain +supported. See docs/bwrap-runtime.md and rein's docs/owner-bootstrap.md. + +The project's `evidence/2026-09-09-owner-bootstrap.json` records the actual candidate +build and isolated installed-interpreter/CLI proof. T04 remains wait for accepted +credential-to-owner delivery, current protected installation and Railiance placement, +provider compatibility and G0. Source packaging does not reopen completed T01-T03, +T05-T06, broaden the old CCRs or activate the previously installed 2.1.263 artifact. + +The first standalone candidate exposed a stale cached local wheel despite current +Git metadata. Owner builds now refresh all local distributions and compare every +installed package source/definition file with its tracked source; stale, missing or +extra files refuse the build. Four content-conformance regressions cover that gap. + + +## 2026-09-27 installed launcher defect and guarded correction + +REINAH-WP-0003's single admitted attempt completed native provider/companion +apply, verify and exec, including scoped-token revocation. The claimed job +`6efa9436-6a91-47c0-94e5-b31b5a0e7e3a` failed before any provider request +reservation. The sandbox was destroyed and the source repository is unchanged. +The parent EUR 10 liability remains held for explicit reconciliation; no retry +or replacement authority is inferred. + +Installed artifact `b6e4e8a4` contains uv-generated three-line shell launchers +that still reference a vanished temporary build interpreter. The builder had +only rewritten direct Python shebangs. Fixed both generated forms and added +actual relocation/execution tests for direct, long and space-containing build +paths, plus refusal of unknown governed launchers. Builder tests: 12 passed; +focused lint passes. Rein's real installed bwrap probe reproduces both broken +entrypoints at exit 127 with zero provider forwards; its new bootstrap checks +refuse the malformed artifact before claiming. + +T04 remains `wait` and this plan stays `blocked`: replacement artifact build/ +admission and a separately authorized successful model/tool/commit proof remain. +The existing SAND-WP-0015 / REINAH-WP-0003 / SECRETS-WP-0009 records retain the +work. No new task or workplan. Detailed non-secret receipts are in rein-aharness +`docs/evidence/2026-09-27-metered-*.json`. + + +## 2026-10-04 offline owner startup proof preparation + +The existing no-credential smoke command now supports +`--require-owner-entrypoints`: the admitted digest-pinned runtime must start both +`rein-aharness --help` and `glas-harness --help`, and imported rein, llm-connect, +Glas and sandboxer modules must originate inside the mounted artifact. Broken or +missing launchers, failed help, timeout and external package origins cannot be +recorded as startup success. The default standalone mode remains supported. +Launcher diagnostics are not copied into the receipt. Focused tests exercise +real subprocesses, including the vanished-build-interpreter failure. + +This is preparation for a corrected immutable candidate's independent native +startup proof; it is not that candidate's receipt or owner acceptance. T04 stays +`wait`, the plan stays `blocked`, and the Sept27 artifact/liability evidence +remains intact. Replacement artifact admission and pins, explicit retained +EUR 10 reconciliation, and a separately authorized successful model/tool/commit +attempt remain with the existing owners. No credentials, provider request, +queue claim, policy activation or retry authorization is supplied here. + +2026-10-04 current blocker reconciliation: September 27 native provider/companion +apply/verify/exec and successful revocation supersede the original missing +platform route. The remaining owner return is rein-aharness corrected artifact/ +profile admission, retained EUR10 reconciliation and fresh retry authority. +T04 stays wait; no renewed trial is authorized. Full owner check: lint clean, +219 tests passed (one existing Starlette deprecation warning). + +## 2026-10-13 owner returns: reconciliation and corrected build + +rein-aharness `d897a83` records owner reconciliation of the Sept 27 held +liability: run `6efa9436` failed at launcher exit 127 before any provider +reservation; liability stays at the full hold, exhausting that one-attempt +envelope, and the owner authorized a new retry envelope on 2026-10-13. +rein-aharness `9a4469d` records corrected runtime `ee3d0a53…` (replaces +`b6e4e8a4…`; only rein moved, adding pre-claim launcher rejection), built with +the sand-boxer builder fix; both launchers exit 0 inside bwrap. Receipts: +rein-aharness `docs/evidence/2026-10-13-*.json`. + +Remaining before T04 closes: install `ee3d0a53` on railiance01, pin its digest +in owner config, pass `--require-owner-entrypoints` offline startup proof, then +run the single authorized attempt to a successful model/tool/commit receipt. +T04 stays `wait`; the plan stays `blocked`. + +2026-10-14 check: rein-aharness pushed `9a4469d` (corrected build record) and +opened HARNESS-WP-0008 (simpler metered retry, active). No railiance01 install/ +admission of `ee3d0a53` or successful acceptance receipt yet; request sent on +hub message `82faf25c`. T04 stays `wait`; plan stays `blocked`. + +2026-10-14 rein-aharness reply (thread `82faf25c`): step 1 done (`9a4469d` on +origin/main). Steps 2-4 are HARNESS-WP-0008-T07. Budget is now up to three +attempts within EUR 10 under memo `infd-20261014-b01` (observed-usage +settlement); order: memo acceptance in the attended informed-decision sitting, +rebuild and install `ee3d0a53` on railiance01, `metered-once --rehearse`, +`owner renew`, first attempt. Railiance01 still pins `b6e4e8a4`. T04 stays `wait`. + +2026-10-15 owner-approved rebuild: the pinned inputs of the `ee3d0a53` receipt +(same four commits, uv.lock `8775904b…`, Claude 2.1.266 `19842705…`) rebuilt +to `818b1b7c8292…f359`. The digest differs only because the builder embeds +absolute source paths (`direct_url.json`, `build-info.json`). Owner-entrypoint +startup smoke passed in sandbox `28df7a1d`; no credentials or model calls. +Sent to rein-aharness (message `8a7962f7`, thread `82faf25c`) to record as the +then-current corrected runtime for HARNESS-WP-0008-T07 and to load the unsigned +memo sitting. Nothing installed on railiance01. T04 stays `wait`. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/glas-harness-driver-reference.patch b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/glas-harness-driver-reference.patch new file mode 100644 index 00000000..a1d58502 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/glas-harness-driver-reference.patch @@ -0,0 +1,11 @@ +--- a/workplans/GLAS-WP-0012-first-local-profile-production-proof.md ++++ b/workplans/GLAS-WP-0012-first-local-profile-production-proof.md +@@ -439,7 +439,7 @@ + Latest owner dependency sources supersede the stale missing-lane account: + September 27 native lanes were activated and revoked; corrected immutable + owner runtime is `818b1b7c…` (October 15 startup proof), not yet installed or +-admitted on Railiance. REINAH-WP-0008-T07 retains memo lifecycle acceptance, ++admitted on Railiance. HARNESS-WP-0008-T07 retains memo lifecycle acceptance, + installation, rehearsal, renewal/pin updates, native approvals and bounded + successful model/tool/commit acceptance. Any subsequent build containing this + patch has a different source/artifact pin requiring owner admission. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/glas-harness-driver-reference.preimage.sha256 b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/glas-harness-driver-reference.preimage.sha256 new file mode 100644 index 00000000..f354ea48 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/glas-harness-driver-reference.preimage.sha256 @@ -0,0 +1 @@ +sha256:881ab0f5ba3bb43f5404c8f4cd23f9628e014992ba88fc13f541f0fa8b7b1eaa diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index-source/AGENTS.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index-source/AGENTS.md new file mode 100644 index 00000000..e165849c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index-source/AGENTS.md @@ -0,0 +1,262 @@ +# rein-aharness — Agent Instructions + +## Repo Identity + +**Purpose:** Claude-Code-CLI-driven rein: governed, unattended/scheduled agent runtime + +**Domain:** infotech +**Repo slug:** rein-aharness +**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a` +**Workplan prefix:** `REINAH-WP-` + +`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from +before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through +`HARNESS-WP-0003` and `HARNESS-WP-ADHOC-2026-09-04` are older ids from the +agent-harness name. Preserve all of these in references and archives, but use +the hub-registered `REINAH-WP-` prefix for every new workplan and task. + +--- + +## Developer Workflow + +```bash +# Install (editable, including the governed profile-execution siblings) +python3 -m venv .venv && source .venv/bin/activate +pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness pytest + +# Test +PYTHONPATH=".:$HOME/llm-connect" python3 -m pytest tests/ -q + +# Run — bounded agentic session against a real target repo (real Claude Code +# CLI call, real commit if successful) +rein-aharness run --task-file examples/task-hello-sandbox.json --no-hub --no-metrics + +# Build the Railiance deploy image +make image +``` + +## State Hub Integration + +The Custodian State Hub tracks work across all domains. Interact via HTTP REST — +there is no MCP server for Codex agents. + +| Context | URL | +|---------|-----| +| Local workstation | `http://127.0.0.1:8000` | +| Remote (railiance01, in-cluster) | `http://10.43.68.154:8000` | +| Optional local edge relay | http://127.0.0.1:18080 | + +When an operator has enabled the edge relay, set API_BASE to the relay URL. +Queueable writes return an explicit queued receipt if the central hub is +unreachable. Treat that as pending local evidence, then ask the operator to run +statehub outbox status/replay after connectivity returns. + +### Orient at session start + +```bash +# Offline brief — works without hub connection +cat .custodian-brief.md + +# Active workplans for this domain +curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \ + | python3 -m json.tool + +# Check inbox +curl -s "http://127.0.0.1:8000/messages/?to_agent=rein-aharness&unread_only=true" \ + | python3 -m json.tool +``` + +Mark a message read: +```bash +curl -s -X PATCH "http://127.0.0.1:8000/messages//read" \ + -H "Content-Type: application/json" -d '{}' +``` + +### Log progress (required at session close) + +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{ + "summary": "what was done", + "event_type": "note", + "author": "codex", + "workplan_id": "", + "task_id": "" + }' +``` + +Omit `workplan_id` / `task_id` when not applicable. + +### Update task status + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"status": "progress"}' +# values: wait | todo | progress | done | cancel +``` + +### Flag a task for human review + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"needs_human": true, "intervention_note": "reason"}' +``` + +--- + +## Session Protocol + +**Start:** +1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe) +2. Check inbox: `GET /messages/?to_agent=rein-aharness&unread_only=true`; mark read +3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks +4. Check human-needed tasks: `GET /tasks/?needs_human=true` + +**During work:** +- Update task statuses in workplan files as tasks progress +- Record significant decisions via `POST /decisions/` + +**Close:** +1. Update workplan file task statuses to reflect progress +2. If finishing a workplan: hand off **residuals** as live work records first + (intake with `origin: residual` + `origin_ref: `, or a next workplan / + decision / engagement). Do not park leftovers only in prose or `SCOPE.md`. + Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals. +3. Log: `POST /progress/` with a summary of what changed (name handoff ids) +4. After workplan file changes, run: + ```bash + statehub fix-consistency + ``` + Coding agents should run this directly; ask the operator only if the CLI or + State Hub API is unavailable. This syncs task status from files into the hub DB. + +--- + +## Credential and access routing + +**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect** +for inference. Run this check **before** requesting secrets, API keys, SSH access, +login tokens, or database passwords — in any repo, not only `ops-warden`. + +ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every +other credential need belongs to another subsystem. **Do not** message +`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key. + +### Lookup (do this first) + +```bash +warden route find "" --json +warden route show --json +``` + +Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`). + +| Agent runtime | How to orient | +| --- | --- | +| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=rein-aharness` is for coordination, not secret vending | +| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership | +| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` | + +### Quick routing table + +| I need… | Owner | ops-warden executes? | +| --- | --- | --- | +| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` | +| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only | +| Login / OIDC / MFA | key-cape / Keycloak | No — route only | +| Authorization decision | flex-auth | No — route only | +| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` | +| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only | + +### Anti-patterns (do not do these) + +- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc. +- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist +- Pasting secrets into Git, State Hub, workplans, logs, or chat + +### Other capabilities (reuse-surface) + +Non-credential capabilities are usually discovered through **reuse-surface** federation +(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in +every repo's agent instructions because it is high-frequency, high-risk, and easy to +get wrong. + +**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml` + + + + +--- + +## Workplan Convention (ADR-001) + +Work items originate as files in this repo — not in the hub. The hub is a +read/cache/index layer that rebuilds from files. + +**File location:** `workplans/REINAH-WP-NNNN-.md` + +**Archived location:** finished workplans may move to +`workplans/archived/YYMMDD-REINAH-WP-NNNN-.md`. The `YYMMDD` prefix is +the completion/archive date; the frontmatter `id` does not change. + +**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use +`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use +this only for low-risk work completed directly; create a normal workplan for +anything needing analysis, design, approval, dependencies, or multiple phases. + +**Frontmatter:** + +```yaml +--- +id: REINAH-WP-NNNN +type: workplan +title: "..." +domain: infotech +repo: rein-aharness +status: proposed | ready | active | blocked | backlog | finished | archived +owner: codex +topic_slug: ... +created: "YYYY-MM-DD" +updated: "YYYY-MM-DD" +state_hub_workstream_id: "" # fix-consistency — do not edit (legacy field name; workplan UUID) +--- +``` + +Use `proposed` for a new draft, `ready` after review against current repo +state, and `finished` after implementation. `stalled` and `needs_review` are +derived health labels, not frontmatter statuses. + +**Terminology:** workplan is the fleet term; `workstream` appears only in legacy +API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see +`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`. + +**Task block format** (one per `##` section): + +``` +## Task Title + +` ` `task +id: REINAH-WP-NNNN-T01 +status: wait | todo | progress | done | cancel +priority: high | medium | low +state_hub_task_id: "" # written by fix-consistency — do not edit +` ` ` + +Task description text. +``` + +Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work. + +**Residuals when finishing:** actionable leftovers become live work records +before `status: finished` — usually an intake (`origin: residual`, +`origin_ref: REINAH-WP-NNNN`) or a spawned workplan. Residual is a *role*, +not a kind. Fleet list lives on State Hub, not in `SCOPE.md`. + +To create a new workplan: +1. Write the file following the format above +2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or + State Hub API is unavailable. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index-source/workplans/REINAH-WP-0008-simpler-metered-retry.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index-source/workplans/REINAH-WP-0008-simpler-metered-retry.md new file mode 100644 index 00000000..3603ca51 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index-source/workplans/REINAH-WP-0008-simpler-metered-retry.md @@ -0,0 +1,354 @@ +--- +id: REINAH-WP-0008 +type: workplan +title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal" +domain: infotech +repo: rein-aharness +status: blocked +owner: claude-code +topic_slug: rein-aharness +priority: high +created: "2026-10-13" +updated: "2026-10-16" +related: + - REINAH-WP-0003 +state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2" +--- + +# REINAH-WP-0008 — Simpler metered retry + +## Origin + +On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436` +(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected +runtime `ee3d0a53…` was built +(`docs/evidence/2026-10-13-corrected-owner-runtime-build.json`). The owner +authorized a new envelope and one retry. + +Preparing that retry took too many owner ceremonies: + +1. The runtime is built on the workstation and copied to railiance01 by hand. +2. `owner.json` and the spend policy hold five digests that the owner edits + by hand. +3. A failed attempt keeps its full hold even when no provider request was + made, so every failure needs a new envelope, ledger and pins. +4. Every attempt needs six native approvals. +5. The launcher bug that failed run `6efa9436` was found only by spending + money. A free check on the host would have caught it. + +The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record +the rest as decisions instead of blocking on them. + +**Goal:** a retry becomes: install the artifact, rehearse for free, approve +once, run. + +## A. Free rehearsal on the host + +```task +id: REINAH-WP-0008-T01 +status: done +priority: high +state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04" +``` + +Add a free rehearsal to the owner check. It runs everything +`metered-once --check` does, then executes the admitted runtime's +`rein-aharness` and `glas-harness` launchers inside bwrap from the mount +path, with no network, credentials, provider route or ledger write. The +sandbox profile is already resolved by `--check`. It prints one receipt with +the runtime digest and each launcher's exit code. + +Today's launcher check reads only the first line of each launcher. This +rehearsal runs them, the same way the failing run did. + +**Done when:** tests prove the rehearsal fails on a runtime whose launchers +point at a missing build directory (the `6efa9436` failure) and passes on a +relocated runtime. It must make no network calls and write nothing. The +owner procedure in `docs/owner-bootstrap.md` makes the rehearsal a required +step before any paid attempt. + +### Implemented — 2026-10-13 + +`rein-aharness metered-once --owner-config --rehearse` (the flag +implies `--check`, so it never reads a provider key or claims). The code is in +`rein_aharness/owner_rehearsal.py`. It uses sand-boxer's isolation flags and +read-only system binds, with the runtime read-only at `/opt/sandboxer/runtime`, +`--unshare-net` and `--clearenv`. It runs each Python launcher with `--help`, +plus `claude --version` when `build-info.json` declares Claude. A missing +bwrap fails closed. + +Tests (`tests/test_owner_rehearsal.py`, `tests/test_owner_bootstrap.py`): + +- With real bwrap, a relocated runtime passes. The uv trampoline that + re-executes a deleted build interpreter fails with 127, as run `6efa9436` + did. +- The rehearsal never claims and never reads the key. +- The command line has no writable bind and no credential. + +The corrected runtime `ee3d0a53…` rehearses clean: rein-aharness 0, +glas-harness 0, claude 0. + +## B. Release holds that provably cost nothing + +```task +id: REINAH-WP-0008-T02 +status: done +priority: high +state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9" +``` + +`SpendLedger.reconcile` keeps the full hold (`max(held, observed)`). Add an +evidence-backed release. If the run has zero provider-request reservations +and its request route is revoked, reconciliation may set the liability to +the observed amount. Otherwise the current rule stands. The release records +which evidence allowed it, and the CLI flag says so, for example +`--release-unused-hold`. + +This changes a deliberate safety rule, so it needs owner sign-off on the +exact rule before merge. That sign-off is recorded in T04's decision note. + +**Done when:** tests cover all four cases. A run with no request +reservations and a revoked route releases. A run with any request +reservation, or with a live route, refuses the release. A second, identical +reconcile changes nothing. The envelope is never marked breached by a +release. + +### Widened to observed-usage settlement — 2026-10-14 + +On 2026-10-13 the owner asked for three attempts within EUR 10 and chose +option B: settle to actual usage instead of releasing only unused holds. +Under full-hold accounting each request holds USD 4.64 (1M context plus +64k output), so EUR 10 cannot fit three attempts. + +Implemented as an opt-in envelope term (`settlement: observed-usage`) plus +`max_runs`; see `docs/owner-bootstrap.md`. Under it: + +- a completed request is charged its gateway-metered cost; +- runs and reconciliations are charged + `max(metered requests, reported or attested cost)`; +- unfinished requests keep their full hold, and overruns still freeze the + envelope. + +The run 6efa9436 shape (failure before any request) now reconciles to its +attested cost. Defaults and existing digests are unchanged. Tests: +`tests/test_spend_settlement.py`, plus renewal cases in +`tests/test_owner_renew.py`. + +Merge-ready, but the rule still needs the owner's acceptance in decision +`a93619a9`. + +## D. One owner command for renewal + +```task +id: REINAH-WP-0008-T03 +status: done +priority: high +state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2" +``` + +Add `rein-aharness owner renew --owner --runtime +--envelope `. It does three things: + +1. It refuses unless every reservation in the current ledger is settled. +2. It writes the new spend policy, initializes a fresh ledger, and updates + the runtime and spend-policy pins in `owner.json` together. Old files are + kept as backups. Existing ledgers are never reset. +3. It prints one summary for the owner to review: old to new runtime, old + to new policy digest, envelope cap and attempt limit, and ledger path. + +Add a dry-run mode that only prints the summary. Afterwards, run the T01 +rehearsal. + +**Done when:** a test renews from the 2026-09-27 layout to a new envelope. +The owner check passes on the result. A second renew with an unsettled +reservation refuses. No digest has to be typed by hand. + +### Implemented — 2026-10-13 + +The command is `rein-aharness owner renew --owner-config --policy --ledger +--runtime-path --envelope [--apply]`, in `rein_aharness/owner_renew.py`. +Without `--apply` it is a dry run. Choices: + +- **Envelope file:** only the envelope fields may change. Worker, target, + profile and grant pins carry over, and `SpendPolicy` validation runs on + the result. +- **Paths stay the same:** the old ledger is renamed to + `.retired-`, never reset, and the old policy and owner.json + are kept as `.bak`. Secrets Engine pins the `exec_owner` files by sha256 + and the ledger path through its fixed environment, so keeping the paths + means the catalog needs only new file hashes. The command prints them. +- **Refusals:** a held reservation, an open request route, an open SQLite + transaction, an owner.json that doesn't match the policy, a reused + envelope id, a disallowed field, or leftovers from an earlier renewal. + +Tests (`tests/test_owner_renew.py`): + +- A dry run changes nothing. +- An applied renewal passes the real owner check (`prepare`) at the + unchanged paths. +- A charged reservation, the railiance01 state after the 2026-10-13 + reconciliation, renews, and the row survives in the retired ledger. +- Bad envelopes and a second apply both refuse. + +Finding for T06: renewal still changes the pinned file hashes, so each +renewal needs a Secrets Engine catalog update and fresh approvals. + +## C. Envelopes that cover several attempts (decision) + +```task +id: REINAH-WP-0008-T04 +status: done +decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043" +priority: medium +state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612" +``` + +Owner decision: should future envelopes be admitted as "up to N attempts +within EUR X" instead of one attempt? The EUR 10 cap already fits two +USD 4.64 request holds. Record the choice, together with T02's sign-off, +as a decision note in `docs/`. T03's envelope format then reflects it. + +### Decided — 2026-10-14 + +The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and +observed-usage settlement, and resolved hub decision `a93619a9` with that +choice. Record: `docs/decisions/2026-10-14-spend-envelope-settlement.md`. +The terms are in spend memo `infd-20261014-b01`. + +## E. Ship the owner runtime as a container image (decision) + +```task +id: REINAH-WP-0008-T05 +status: wait +needs_human: true +decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5" +priority: low +state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee" +``` + +Decision for later, with Platform (railiance) and Sand-boxer: should the +owner runtime ship through the registry and GitOps, using the existing +`make image` path, instead of rsync and a manual digest check? Out of scope +here. If accepted, hand it off as a residual intake to the owning repos. + +## F. Approvals per envelope (decision) + +```task +id: REINAH-WP-0008-T06 +status: wait +needs_human: true +decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b" +priority: low +state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af" +``` + +Decision for later, with Secrets Engine: should one approval set cover an +envelope's attempts instead of six native approvals per attempt? The work +lives outside this repo. If accepted, hand it off as a residual intake. + +## Retry on the corrected runtime + +```task +id: REINAH-WP-0008-T07 +status: wait +needs_human: true +blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01" +priority: high +state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b" +``` + +Waits on T01 and T03. It does not need T02. + +Steps: + +1. Rebuild `ee3d0a53…`, or the then-current corrected runtime, from the + pinned commits. +2. Install it on railiance01. +3. Run the T01 rehearsal. +4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized + 2026-10-13). +5. Run exactly one attended attempt, which consumes fresh native approvals. + +Record the result in REINAH-WP-0003-T05/T06. + +### Waiting on the owner — 2026-10-14 + +Everything this repo can do is done: the rehearsal (T01), the settlement rule +(T02, T04) and renewal (T03) are in place. Draft memo `infd-20261014-b01` is +in informed-decision `ee6a65b`. What remains needs the owner, in order: + +1. Accept the memo in the attended sitting. +2. ~~Rebuild `ee3d0a53` from the pinned commits~~ (done 2026-10-15 as + `818b1b7c…`, see below) and install it on railiance01. +3. Run `metered-once --rehearse`. +4. Run `owner renew` with the memo's terms, then update the Secrets Engine + file pins. +5. Approve the native actions and run the first attempt. + +Sand-boxer's steps 2–4 in message `82faf25c` are this same sequence. + +### Runtime rebuilt — 2026-10-15 + +Step 1 is done. The owner approved a rebuild because `ee3d0a53…` was not kept +on the workstation. Sand-boxer built `818b1b7c…` from the same pinned inputs +and builder. The digest differs only because the build-source paths are +embedded in the artifact. The bwrap startup smoke passed (sandbox `28df7a1d`) +with no credentials and no model run. `818b1b7c…` is now the install +candidate. It is not installed on railiance01 (still `b6e4e8a4…`) or admitted. +Receipt: `docs/evidence/2026-10-15-rebuilt-owner-runtime.json`. The memo still +still has to be created, admitted, loaded and accepted first. The owner steps +are in informed-decision `docs/batches/2026-10-14-three-attempts/OPERATOR.md`. + +### Memo queued — 2026-10-15 + +Owner step 1 is in progress via the lifecycle intake path (owner's choice; the +review-record approval counts as spend authority). Workspace draft +`2ccb4bda-e318-51d2-a8dd-709db768494f`, review +`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`, queued by the owner in the State +Hub memo dispatcher; approval intent `accept` by `helixforge-factory`. Record: +informed-decision `d5f1021` (sitting.json, OPERATOR.md). The accepted approval id +becomes `authority_ref` for `owner renew`. Install candidate remains `818b1b7c`. + + +### Workload-only Glas source integration — 2026-10-16 + +Reviewed and integrated the one-line runtime-contract lock advance from Glas +`30be81808140a3a3d137e731d1fe9aed59ad8581` to published workload-only credential +delivery `7357115c8311e55ee3d495f5266f501a17ec3c89`. llm-connect `37436bb5` and +sandboxer `221126cb` remain pinned. This is source integration under T07, not +native artifact installation or admission. T07 remains waiting with its existing +owner gates. + +Prior preparation used rein base `be5ef400` plus isolated lock-only commit +`c811fc43` to build candidate +`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`. The +candidate's installed packages passed actual SandboxManager/Glas gateway proof +with unchanged `harness.agent-dev-local@1.1.1` (still unverified): real rein/Glas +CLI and Claude 2.1.266 startup, read-only runtime, synthetic host-file and +inherited environment/child-process denial, exact task identity/private state, +deterministic workspace Git effect, task removal and destruction. A separately +labelled synthetic route fixture passed workload-only delivery, output redaction +and clean subsequent housekeeping through the same actual manager. Wrong +actor/project/run, expired execution, expiry cleanup, cancellation and +post-cancellation refusal passed. All 141 Glas tests and seven rein lock/rehearsal +tests passed; whole artifact digest remained unchanged after proof. + +The first proof runner correctly encountered the manager's PermissionError for +wrong consumer but expected ValueError/RuntimeError. Its finally cleanup ran; +the runner catch was refined and the failure retained. This does not earn an +unchanged execution success. No model/provider request, real credential read, +queue access or spend occurred. + +The preparation artifact is not the artifact of this integrated owner revision. +Rebuild from the exact committed owner source and verify its new digest and +actual-manager proof before considering installation. Source inputs, receipts, +reproducible proof and attempt history are retained in the +[Custodian evidence directory](../../the-custodian/docs/changes/netkingdom-target-2026-10-16/agent-isolation-next/README.md). +The same directory records the integrated-source rebuild separately. + +Retain `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their outcomes as historical +evidence. Native acceptance still requires the queued spend memo lifecycle, +owner artifact installation/rehearsal, renewal and Secrets Engine file-pin +updates, required native approvals and bounded real-model/tool proof. Actual +interactive assignment migration and external tool-path denial remain Custodian +acceptance obligations; no profile readiness or supervision authority changed. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index.preimage b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index.preimage new file mode 100644 index 00000000..a34993e1 Binary files /dev/null and b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/index.preimage differ diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/manifest.json new file mode 100644 index 00000000..36b8efa9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/manifest.json @@ -0,0 +1,27 @@ +{ + "schema": "custodian.source-identity-correction.v1", + "status": "source_applied_publication_pending", + "files": { + "HEAD-source/AGENTS.md": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034", + "HEAD-source/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68", + "current-driver-patch-selection.json": "sha256:02f3e03ebbd59f307bc9c1651f824beec2f0f3240a894f7f34e243ec060c3e6c", + "driver-proposals/glas-harness/workplans/GLAS-WP-0012-first-local-profile-production-proof.md": "sha256:23f97bc183bef2b0f38eb40c64bc0bb010df1c1eb4596ff3b5e0f73da7c2fcfc", + "driver-proposals/sand-boxer/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md": "sha256:10ff0a59b155bccd15bc2432619142062f6d04052deabb7cd8a796d908c894ca", + "driver-proposals/sand-boxer-current/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md": "sha256:56022703fd4d973f40ddad8eb99cb02783b086549b24ef89f89157de33eafe31", + "glas-harness-driver-reference.patch": "sha256:fb4518d2c1ed022230bbb2955970c20fcd288141dcd88718526defaca0895b2a", + "glas-harness-driver-reference.preimage.sha256": "sha256:de5afac619d1863d9340961c321effbda3677cdfcb765e24d4e5c4bfe673095c", + "index-source/AGENTS.md": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034", + "index-source/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68", + "index.preimage": "sha256:08c186d73338c3eb03a8216c7025fdb6cb386a647802f4df7f096c2f177ff98c", + "postimages/AGENTS.md": "sha256:da71c6e3d76d7c867b2db6e67cd919fba594acc3804d3784dd744438103560c2", + "postimages/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:1118924bb159f8a52a37ace8b8663c75274b89928f7a8b957638428af224b054", + "preimages/AGENTS.md": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034", + "preimages/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68", + "preparation.json": "sha256:758faed4c436d70106bd9d109a440595bea4e744d3066e1bab1d2080edcd165b", + "rein-aharness-source-correction.patch": "sha256:16c8223d22621aaccf3f430b88d1b3ec0178b60a4d4f210a34948d677f4f2441", + "sand-boxer-current-driver-reference.patch": "sha256:0c4d631655b7657cadcf8be51861a4d95edb84ccc8a0c8b8cc39f7e66549da5f", + "sand-boxer-driver-reference.patch": "sha256:63f234472f1728cdb10bdd24e27f90e05b5f3f17c91bc87eb9f973bcfd2ff8ff", + "sand-boxer-driver-reference.preimage.sha256": "sha256:5bbe7e322db7c9279240de942ebb9ebc5eca11e6945540e8d0bb59de3e0e33b8", + "verification.json": "sha256:a32b481c98e10fe10678c49b71b06fa62b2b4b8c4dc040c4d2f5a6732463ce09" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/postimages/AGENTS.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/postimages/AGENTS.md new file mode 100644 index 00000000..537cbaa4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/postimages/AGENTS.md @@ -0,0 +1,264 @@ +# rein-aharness — Agent Instructions + +## Repo Identity + +**Purpose:** Claude-Code-CLI-driven rein: governed, unattended/scheduled agent runtime + +**Domain:** infotech +**Repo slug:** rein-aharness +**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a` +**Workplan prefix:** `REINAH-WP-` + +`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from +before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through +`HARNESS-WP-0003`, `HARNESS-WP-0008` and +`HARNESS-WP-ADHOC-2026-09-04` are preserved historical ids. Workplan 0008 +keeps its originally issued HARNESS identity after the 2026-10-17 correction +of an erroneous REINAH relabel; its existing REINAH filename is only a path. Preserve all of these in references and archives, but use +the hub-registered `REINAH-WP-` prefix for every new workplan and task. + +--- + +## Developer Workflow + +```bash +# Install (editable, including the governed profile-execution siblings) +python3 -m venv .venv && source .venv/bin/activate +pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness pytest + +# Test +PYTHONPATH=".:$HOME/llm-connect" python3 -m pytest tests/ -q + +# Run — bounded agentic session against a real target repo (real Claude Code +# CLI call, real commit if successful) +rein-aharness run --task-file examples/task-hello-sandbox.json --no-hub --no-metrics + +# Build the Railiance deploy image +make image +``` + +## State Hub Integration + +The Custodian State Hub tracks work across all domains. Interact via HTTP REST — +there is no MCP server for Codex agents. + +| Context | URL | +|---------|-----| +| Local workstation | `http://127.0.0.1:8000` | +| Remote (railiance01, in-cluster) | `http://10.43.68.154:8000` | +| Optional local edge relay | http://127.0.0.1:18080 | + +When an operator has enabled the edge relay, set API_BASE to the relay URL. +Queueable writes return an explicit queued receipt if the central hub is +unreachable. Treat that as pending local evidence, then ask the operator to run +statehub outbox status/replay after connectivity returns. + +### Orient at session start + +```bash +# Offline brief — works without hub connection +cat .custodian-brief.md + +# Active workplans for this domain +curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \ + | python3 -m json.tool + +# Check inbox +curl -s "http://127.0.0.1:8000/messages/?to_agent=rein-aharness&unread_only=true" \ + | python3 -m json.tool +``` + +Mark a message read: +```bash +curl -s -X PATCH "http://127.0.0.1:8000/messages//read" \ + -H "Content-Type: application/json" -d '{}' +``` + +### Log progress (required at session close) + +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{ + "summary": "what was done", + "event_type": "note", + "author": "codex", + "workplan_id": "", + "task_id": "" + }' +``` + +Omit `workplan_id` / `task_id` when not applicable. + +### Update task status + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"status": "progress"}' +# values: wait | todo | progress | done | cancel +``` + +### Flag a task for human review + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"needs_human": true, "intervention_note": "reason"}' +``` + +--- + +## Session Protocol + +**Start:** +1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe) +2. Check inbox: `GET /messages/?to_agent=rein-aharness&unread_only=true`; mark read +3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks +4. Check human-needed tasks: `GET /tasks/?needs_human=true` + +**During work:** +- Update task statuses in workplan files as tasks progress +- Record significant decisions via `POST /decisions/` + +**Close:** +1. Update workplan file task statuses to reflect progress +2. If finishing a workplan: hand off **residuals** as live work records first + (intake with `origin: residual` + `origin_ref: `, or a next workplan / + decision / engagement). Do not park leftovers only in prose or `SCOPE.md`. + Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals. +3. Log: `POST /progress/` with a summary of what changed (name handoff ids) +4. After workplan file changes, run: + ```bash + statehub fix-consistency + ``` + Coding agents should run this directly; ask the operator only if the CLI or + State Hub API is unavailable. This syncs task status from files into the hub DB. + +--- + +## Credential and access routing + +**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect** +for inference. Run this check **before** requesting secrets, API keys, SSH access, +login tokens, or database passwords — in any repo, not only `ops-warden`. + +ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every +other credential need belongs to another subsystem. **Do not** message +`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key. + +### Lookup (do this first) + +```bash +warden route find "" --json +warden route show --json +``` + +Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`). + +| Agent runtime | How to orient | +| --- | --- | +| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=rein-aharness` is for coordination, not secret vending | +| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership | +| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` | + +### Quick routing table + +| I need… | Owner | ops-warden executes? | +| --- | --- | --- | +| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` | +| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only | +| Login / OIDC / MFA | key-cape / Keycloak | No — route only | +| Authorization decision | flex-auth | No — route only | +| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` | +| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only | + +### Anti-patterns (do not do these) + +- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc. +- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist +- Pasting secrets into Git, State Hub, workplans, logs, or chat + +### Other capabilities (reuse-surface) + +Non-credential capabilities are usually discovered through **reuse-surface** federation +(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in +every repo's agent instructions because it is high-frequency, high-risk, and easy to +get wrong. + +**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml` + + + + +--- + +## Workplan Convention (ADR-001) + +Work items originate as files in this repo — not in the hub. The hub is a +read/cache/index layer that rebuilds from files. + +**File location:** `workplans/REINAH-WP-NNNN-.md` + +**Archived location:** finished workplans may move to +`workplans/archived/YYMMDD-REINAH-WP-NNNN-.md`. The `YYMMDD` prefix is +the completion/archive date; the frontmatter `id` does not change. + +**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use +`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use +this only for low-risk work completed directly; create a normal workplan for +anything needing analysis, design, approval, dependencies, or multiple phases. + +**Frontmatter:** + +```yaml +--- +id: REINAH-WP-NNNN +type: workplan +title: "..." +domain: infotech +repo: rein-aharness +status: proposed | ready | active | blocked | backlog | finished | archived +owner: codex +topic_slug: ... +created: "YYYY-MM-DD" +updated: "YYYY-MM-DD" +state_hub_workstream_id: "" # fix-consistency — do not edit (legacy field name; workplan UUID) +--- +``` + +Use `proposed` for a new draft, `ready` after review against current repo +state, and `finished` after implementation. `stalled` and `needs_review` are +derived health labels, not frontmatter statuses. + +**Terminology:** workplan is the fleet term; `workstream` appears only in legacy +API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see +`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`. + +**Task block format** (one per `##` section): + +``` +## Task Title + +` ` `task +id: REINAH-WP-NNNN-T01 +status: wait | todo | progress | done | cancel +priority: high | medium | low +state_hub_task_id: "" # written by fix-consistency — do not edit +` ` ` + +Task description text. +``` + +Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work. + +**Residuals when finishing:** actionable leftovers become live work records +before `status: finished` — usually an intake (`origin: residual`, +`origin_ref: REINAH-WP-NNNN`) or a spawned workplan. Residual is a *role*, +not a kind. Fleet list lives on State Hub, not in `SCOPE.md`. + +To create a new workplan: +1. Write the file following the format above +2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or + State Hub API is unavailable. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/postimages/workplans/REINAH-WP-0008-simpler-metered-retry.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/postimages/workplans/REINAH-WP-0008-simpler-metered-retry.md new file mode 100644 index 00000000..98e8d212 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/postimages/workplans/REINAH-WP-0008-simpler-metered-retry.md @@ -0,0 +1,365 @@ +--- +id: HARNESS-WP-0008 +type: workplan +title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal" +domain: infotech +repo: rein-aharness +status: blocked +owner: claude-code +topic_slug: rein-aharness +priority: high +created: "2026-10-13" +updated: "2026-10-17" +related: + - REINAH-WP-0003 +state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2" +--- + +# HARNESS-WP-0008 — Simpler metered retry + +## Origin + +Identity correction, 2026-10-17: restore the originally issued +`HARNESS-WP-0008` and T01–T07 canonical IDs, retaining every existing Hub UUID, +status, decision and human/native gate. Commit `a85de661` briefly relabelled +this workplan `REINAH-WP-0008` while preserving HARNESS-derived UUIDs in response +to a C-26 warning. Canon grandfathering preserves existing IDs; C-26 applies +to new plans and supplies no record-rekey authority. The REINAH filename remains +as an existing backing path, and the erroneous label remains historical +provenance rather than another canonical record. Future new workplans still +use REINAH-WP. No UUID migration, retirement, human disposition, installation, +spend authority or native acceptance is granted by this source correction. + +On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436` +(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected +runtime `ee3d0a53…` was built +(`docs/evidence/2026-10-13-corrected-owner-runtime-build.json`). The owner +authorized a new envelope and one retry. + +Preparing that retry took too many owner ceremonies: + +1. The runtime is built on the workstation and copied to railiance01 by hand. +2. `owner.json` and the spend policy hold five digests that the owner edits + by hand. +3. A failed attempt keeps its full hold even when no provider request was + made, so every failure needs a new envelope, ledger and pins. +4. Every attempt needs six native approvals. +5. The launcher bug that failed run `6efa9436` was found only by spending + money. A free check on the host would have caught it. + +The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record +the rest as decisions instead of blocking on them. + +**Goal:** a retry becomes: install the artifact, rehearse for free, approve +once, run. + +## A. Free rehearsal on the host + +```task +id: HARNESS-WP-0008-T01 +status: done +priority: high +state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04" +``` + +Add a free rehearsal to the owner check. It runs everything +`metered-once --check` does, then executes the admitted runtime's +`rein-aharness` and `glas-harness` launchers inside bwrap from the mount +path, with no network, credentials, provider route or ledger write. The +sandbox profile is already resolved by `--check`. It prints one receipt with +the runtime digest and each launcher's exit code. + +Today's launcher check reads only the first line of each launcher. This +rehearsal runs them, the same way the failing run did. + +**Done when:** tests prove the rehearsal fails on a runtime whose launchers +point at a missing build directory (the `6efa9436` failure) and passes on a +relocated runtime. It must make no network calls and write nothing. The +owner procedure in `docs/owner-bootstrap.md` makes the rehearsal a required +step before any paid attempt. + +### Implemented — 2026-10-13 + +`rein-aharness metered-once --owner-config --rehearse` (the flag +implies `--check`, so it never reads a provider key or claims). The code is in +`rein_aharness/owner_rehearsal.py`. It uses sand-boxer's isolation flags and +read-only system binds, with the runtime read-only at `/opt/sandboxer/runtime`, +`--unshare-net` and `--clearenv`. It runs each Python launcher with `--help`, +plus `claude --version` when `build-info.json` declares Claude. A missing +bwrap fails closed. + +Tests (`tests/test_owner_rehearsal.py`, `tests/test_owner_bootstrap.py`): + +- With real bwrap, a relocated runtime passes. The uv trampoline that + re-executes a deleted build interpreter fails with 127, as run `6efa9436` + did. +- The rehearsal never claims and never reads the key. +- The command line has no writable bind and no credential. + +The corrected runtime `ee3d0a53…` rehearses clean: rein-aharness 0, +glas-harness 0, claude 0. + +## B. Release holds that provably cost nothing + +```task +id: HARNESS-WP-0008-T02 +status: done +priority: high +state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9" +``` + +`SpendLedger.reconcile` keeps the full hold (`max(held, observed)`). Add an +evidence-backed release. If the run has zero provider-request reservations +and its request route is revoked, reconciliation may set the liability to +the observed amount. Otherwise the current rule stands. The release records +which evidence allowed it, and the CLI flag says so, for example +`--release-unused-hold`. + +This changes a deliberate safety rule, so it needs owner sign-off on the +exact rule before merge. That sign-off is recorded in T04's decision note. + +**Done when:** tests cover all four cases. A run with no request +reservations and a revoked route releases. A run with any request +reservation, or with a live route, refuses the release. A second, identical +reconcile changes nothing. The envelope is never marked breached by a +release. + +### Widened to observed-usage settlement — 2026-10-14 + +On 2026-10-13 the owner asked for three attempts within EUR 10 and chose +option B: settle to actual usage instead of releasing only unused holds. +Under full-hold accounting each request holds USD 4.64 (1M context plus +64k output), so EUR 10 cannot fit three attempts. + +Implemented as an opt-in envelope term (`settlement: observed-usage`) plus +`max_runs`; see `docs/owner-bootstrap.md`. Under it: + +- a completed request is charged its gateway-metered cost; +- runs and reconciliations are charged + `max(metered requests, reported or attested cost)`; +- unfinished requests keep their full hold, and overruns still freeze the + envelope. + +The run 6efa9436 shape (failure before any request) now reconciles to its +attested cost. Defaults and existing digests are unchanged. Tests: +`tests/test_spend_settlement.py`, plus renewal cases in +`tests/test_owner_renew.py`. + +Merge-ready, but the rule still needs the owner's acceptance in decision +`a93619a9`. + +## D. One owner command for renewal + +```task +id: HARNESS-WP-0008-T03 +status: done +priority: high +state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2" +``` + +Add `rein-aharness owner renew --owner --runtime +--envelope `. It does three things: + +1. It refuses unless every reservation in the current ledger is settled. +2. It writes the new spend policy, initializes a fresh ledger, and updates + the runtime and spend-policy pins in `owner.json` together. Old files are + kept as backups. Existing ledgers are never reset. +3. It prints one summary for the owner to review: old to new runtime, old + to new policy digest, envelope cap and attempt limit, and ledger path. + +Add a dry-run mode that only prints the summary. Afterwards, run the T01 +rehearsal. + +**Done when:** a test renews from the 2026-09-27 layout to a new envelope. +The owner check passes on the result. A second renew with an unsettled +reservation refuses. No digest has to be typed by hand. + +### Implemented — 2026-10-13 + +The command is `rein-aharness owner renew --owner-config --policy --ledger +--runtime-path --envelope [--apply]`, in `rein_aharness/owner_renew.py`. +Without `--apply` it is a dry run. Choices: + +- **Envelope file:** only the envelope fields may change. Worker, target, + profile and grant pins carry over, and `SpendPolicy` validation runs on + the result. +- **Paths stay the same:** the old ledger is renamed to + `.retired-`, never reset, and the old policy and owner.json + are kept as `.bak`. Secrets Engine pins the `exec_owner` files by sha256 + and the ledger path through its fixed environment, so keeping the paths + means the catalog needs only new file hashes. The command prints them. +- **Refusals:** a held reservation, an open request route, an open SQLite + transaction, an owner.json that doesn't match the policy, a reused + envelope id, a disallowed field, or leftovers from an earlier renewal. + +Tests (`tests/test_owner_renew.py`): + +- A dry run changes nothing. +- An applied renewal passes the real owner check (`prepare`) at the + unchanged paths. +- A charged reservation, the railiance01 state after the 2026-10-13 + reconciliation, renews, and the row survives in the retired ledger. +- Bad envelopes and a second apply both refuse. + +Finding for T06: renewal still changes the pinned file hashes, so each +renewal needs a Secrets Engine catalog update and fresh approvals. + +## C. Envelopes that cover several attempts (decision) + +```task +id: HARNESS-WP-0008-T04 +status: done +decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043" +priority: medium +state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612" +``` + +Owner decision: should future envelopes be admitted as "up to N attempts +within EUR X" instead of one attempt? The EUR 10 cap already fits two +USD 4.64 request holds. Record the choice, together with T02's sign-off, +as a decision note in `docs/`. T03's envelope format then reflects it. + +### Decided — 2026-10-14 + +The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and +observed-usage settlement, and resolved hub decision `a93619a9` with that +choice. Record: `docs/decisions/2026-10-14-spend-envelope-settlement.md`. +The terms are in spend memo `infd-20261014-b01`. + +## E. Ship the owner runtime as a container image (decision) + +```task +id: HARNESS-WP-0008-T05 +status: wait +needs_human: true +decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5" +priority: low +state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee" +``` + +Decision for later, with Platform (railiance) and Sand-boxer: should the +owner runtime ship through the registry and GitOps, using the existing +`make image` path, instead of rsync and a manual digest check? Out of scope +here. If accepted, hand it off as a residual intake to the owning repos. + +## F. Approvals per envelope (decision) + +```task +id: HARNESS-WP-0008-T06 +status: wait +needs_human: true +decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b" +priority: low +state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af" +``` + +Decision for later, with Secrets Engine: should one approval set cover an +envelope's attempts instead of six native approvals per attempt? The work +lives outside this repo. If accepted, hand it off as a residual intake. + +## Retry on the corrected runtime + +```task +id: HARNESS-WP-0008-T07 +status: wait +needs_human: true +blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01" +priority: high +state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b" +``` + +Waits on T01 and T03. It does not need T02. + +Steps: + +1. Rebuild `ee3d0a53…`, or the then-current corrected runtime, from the + pinned commits. +2. Install it on railiance01. +3. Run the T01 rehearsal. +4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized + 2026-10-13). +5. Run exactly one attended attempt, which consumes fresh native approvals. + +Record the result in REINAH-WP-0003-T05/T06. + +### Waiting on the owner — 2026-10-14 + +Everything this repo can do is done: the rehearsal (T01), the settlement rule +(T02, T04) and renewal (T03) are in place. Draft memo `infd-20261014-b01` is +in informed-decision `ee6a65b`. What remains needs the owner, in order: + +1. Accept the memo in the attended sitting. +2. ~~Rebuild `ee3d0a53` from the pinned commits~~ (done 2026-10-15 as + `818b1b7c…`, see below) and install it on railiance01. +3. Run `metered-once --rehearse`. +4. Run `owner renew` with the memo's terms, then update the Secrets Engine + file pins. +5. Approve the native actions and run the first attempt. + +Sand-boxer's steps 2–4 in message `82faf25c` are this same sequence. + +### Runtime rebuilt — 2026-10-15 + +Step 1 is done. The owner approved a rebuild because `ee3d0a53…` was not kept +on the workstation. Sand-boxer built `818b1b7c…` from the same pinned inputs +and builder. The digest differs only because the build-source paths are +embedded in the artifact. The bwrap startup smoke passed (sandbox `28df7a1d`) +with no credentials and no model run. `818b1b7c…` is now the install +candidate. It is not installed on railiance01 (still `b6e4e8a4…`) or admitted. +Receipt: `docs/evidence/2026-10-15-rebuilt-owner-runtime.json`. The memo still +still has to be created, admitted, loaded and accepted first. The owner steps +are in informed-decision `docs/batches/2026-10-14-three-attempts/OPERATOR.md`. + +### Memo queued — 2026-10-15 + +Owner step 1 is in progress via the lifecycle intake path (owner's choice; the +review-record approval counts as spend authority). Workspace draft +`2ccb4bda-e318-51d2-a8dd-709db768494f`, review +`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`, queued by the owner in the State +Hub memo dispatcher; approval intent `accept` by `helixforge-factory`. Record: +informed-decision `d5f1021` (sitting.json, OPERATOR.md). The accepted approval id +becomes `authority_ref` for `owner renew`. Install candidate remains `818b1b7c`. + + +### Workload-only Glas source integration — 2026-10-16 + +Reviewed and integrated the one-line runtime-contract lock advance from Glas +`30be81808140a3a3d137e731d1fe9aed59ad8581` to published workload-only credential +delivery `7357115c8311e55ee3d495f5266f501a17ec3c89`. llm-connect `37436bb5` and +sandboxer `221126cb` remain pinned. This is source integration under T07, not +native artifact installation or admission. T07 remains waiting with its existing +owner gates. + +Prior preparation used rein base `be5ef400` plus isolated lock-only commit +`c811fc43` to build candidate +`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`. The +candidate's installed packages passed actual SandboxManager/Glas gateway proof +with unchanged `harness.agent-dev-local@1.1.1` (still unverified): real rein/Glas +CLI and Claude 2.1.266 startup, read-only runtime, synthetic host-file and +inherited environment/child-process denial, exact task identity/private state, +deterministic workspace Git effect, task removal and destruction. A separately +labelled synthetic route fixture passed workload-only delivery, output redaction +and clean subsequent housekeeping through the same actual manager. Wrong +actor/project/run, expired execution, expiry cleanup, cancellation and +post-cancellation refusal passed. All 141 Glas tests and seven rein lock/rehearsal +tests passed; whole artifact digest remained unchanged after proof. + +The first proof runner correctly encountered the manager's PermissionError for +wrong consumer but expected ValueError/RuntimeError. Its finally cleanup ran; +the runner catch was refined and the failure retained. This does not earn an +unchanged execution success. No model/provider request, real credential read, +queue access or spend occurred. + +The preparation artifact is not the artifact of this integrated owner revision. +Rebuild from the exact committed owner source and verify its new digest and +actual-manager proof before considering installation. Source inputs, receipts, +reproducible proof and attempt history are retained in the +[Custodian evidence directory](../../the-custodian/docs/changes/netkingdom-target-2026-10-16/agent-isolation-next/README.md). +The same directory records the integrated-source rebuild separately. + +Retain `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their outcomes as historical +evidence. Native acceptance still requires the queued spend memo lifecycle, +owner artifact installation/rehearsal, renewal and Secrets Engine file-pin +updates, required native approvals and bounded real-model/tool proof. Actual +interactive assignment migration and external tool-path denial remain Custodian +acceptance obligations; no profile readiness or supervision authority changed. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preimages/AGENTS.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preimages/AGENTS.md new file mode 100644 index 00000000..e165849c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preimages/AGENTS.md @@ -0,0 +1,262 @@ +# rein-aharness — Agent Instructions + +## Repo Identity + +**Purpose:** Claude-Code-CLI-driven rein: governed, unattended/scheduled agent runtime + +**Domain:** infotech +**Repo slug:** rein-aharness +**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a` +**Workplan prefix:** `REINAH-WP-` + +`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from +before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through +`HARNESS-WP-0003` and `HARNESS-WP-ADHOC-2026-09-04` are older ids from the +agent-harness name. Preserve all of these in references and archives, but use +the hub-registered `REINAH-WP-` prefix for every new workplan and task. + +--- + +## Developer Workflow + +```bash +# Install (editable, including the governed profile-execution siblings) +python3 -m venv .venv && source .venv/bin/activate +pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness pytest + +# Test +PYTHONPATH=".:$HOME/llm-connect" python3 -m pytest tests/ -q + +# Run — bounded agentic session against a real target repo (real Claude Code +# CLI call, real commit if successful) +rein-aharness run --task-file examples/task-hello-sandbox.json --no-hub --no-metrics + +# Build the Railiance deploy image +make image +``` + +## State Hub Integration + +The Custodian State Hub tracks work across all domains. Interact via HTTP REST — +there is no MCP server for Codex agents. + +| Context | URL | +|---------|-----| +| Local workstation | `http://127.0.0.1:8000` | +| Remote (railiance01, in-cluster) | `http://10.43.68.154:8000` | +| Optional local edge relay | http://127.0.0.1:18080 | + +When an operator has enabled the edge relay, set API_BASE to the relay URL. +Queueable writes return an explicit queued receipt if the central hub is +unreachable. Treat that as pending local evidence, then ask the operator to run +statehub outbox status/replay after connectivity returns. + +### Orient at session start + +```bash +# Offline brief — works without hub connection +cat .custodian-brief.md + +# Active workplans for this domain +curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \ + | python3 -m json.tool + +# Check inbox +curl -s "http://127.0.0.1:8000/messages/?to_agent=rein-aharness&unread_only=true" \ + | python3 -m json.tool +``` + +Mark a message read: +```bash +curl -s -X PATCH "http://127.0.0.1:8000/messages//read" \ + -H "Content-Type: application/json" -d '{}' +``` + +### Log progress (required at session close) + +```bash +curl -s -X POST http://127.0.0.1:8000/progress/ \ + -H "Content-Type: application/json" \ + -d '{ + "summary": "what was done", + "event_type": "note", + "author": "codex", + "workplan_id": "", + "task_id": "" + }' +``` + +Omit `workplan_id` / `task_id` when not applicable. + +### Update task status + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"status": "progress"}' +# values: wait | todo | progress | done | cancel +``` + +### Flag a task for human review + +```bash +curl -s -X PATCH "http://127.0.0.1:8000/tasks/" \ + -H "Content-Type: application/json" \ + -d '{"needs_human": true, "intervention_note": "reason"}' +``` + +--- + +## Session Protocol + +**Start:** +1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe) +2. Check inbox: `GET /messages/?to_agent=rein-aharness&unread_only=true`; mark read +3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks +4. Check human-needed tasks: `GET /tasks/?needs_human=true` + +**During work:** +- Update task statuses in workplan files as tasks progress +- Record significant decisions via `POST /decisions/` + +**Close:** +1. Update workplan file task statuses to reflect progress +2. If finishing a workplan: hand off **residuals** as live work records first + (intake with `origin: residual` + `origin_ref: `, or a next workplan / + decision / engagement). Do not park leftovers only in prose or `SCOPE.md`. + Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals. +3. Log: `POST /progress/` with a summary of what changed (name handoff ids) +4. After workplan file changes, run: + ```bash + statehub fix-consistency + ``` + Coding agents should run this directly; ask the operator only if the CLI or + State Hub API is unavailable. This syncs task status from files into the hub DB. + +--- + +## Credential and access routing + +**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect** +for inference. Run this check **before** requesting secrets, API keys, SSH access, +login tokens, or database passwords — in any repo, not only `ops-warden`. + +ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every +other credential need belongs to another subsystem. **Do not** message +`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key. + +### Lookup (do this first) + +```bash +warden route find "" --json +warden route show --json +``` + +Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`). + +| Agent runtime | How to orient | +| --- | --- | +| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=rein-aharness` is for coordination, not secret vending | +| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership | +| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` | + +### Quick routing table + +| I need… | Owner | ops-warden executes? | +| --- | --- | --- | +| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` | +| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only | +| Login / OIDC / MFA | key-cape / Keycloak | No — route only | +| Authorization decision | flex-auth | No — route only | +| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` | +| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only | + +### Anti-patterns (do not do these) + +- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc. +- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist +- Pasting secrets into Git, State Hub, workplans, logs, or chat + +### Other capabilities (reuse-surface) + +Non-credential capabilities are usually discovered through **reuse-surface** federation +(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in +every repo's agent instructions because it is high-frequency, high-risk, and easy to +get wrong. + +**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml` + + + + +--- + +## Workplan Convention (ADR-001) + +Work items originate as files in this repo — not in the hub. The hub is a +read/cache/index layer that rebuilds from files. + +**File location:** `workplans/REINAH-WP-NNNN-.md` + +**Archived location:** finished workplans may move to +`workplans/archived/YYMMDD-REINAH-WP-NNNN-.md`. The `YYMMDD` prefix is +the completion/archive date; the frontmatter `id` does not change. + +**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use +`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use +this only for low-risk work completed directly; create a normal workplan for +anything needing analysis, design, approval, dependencies, or multiple phases. + +**Frontmatter:** + +```yaml +--- +id: REINAH-WP-NNNN +type: workplan +title: "..." +domain: infotech +repo: rein-aharness +status: proposed | ready | active | blocked | backlog | finished | archived +owner: codex +topic_slug: ... +created: "YYYY-MM-DD" +updated: "YYYY-MM-DD" +state_hub_workstream_id: "" # fix-consistency — do not edit (legacy field name; workplan UUID) +--- +``` + +Use `proposed` for a new draft, `ready` after review against current repo +state, and `finished` after implementation. `stalled` and `needs_review` are +derived health labels, not frontmatter statuses. + +**Terminology:** workplan is the fleet term; `workstream` appears only in legacy +API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see +`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`. + +**Task block format** (one per `##` section): + +``` +## Task Title + +` ` `task +id: REINAH-WP-NNNN-T01 +status: wait | todo | progress | done | cancel +priority: high | medium | low +state_hub_task_id: "" # written by fix-consistency — do not edit +` ` ` + +Task description text. +``` + +Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work. + +**Residuals when finishing:** actionable leftovers become live work records +before `status: finished` — usually an intake (`origin: residual`, +`origin_ref: REINAH-WP-NNNN`) or a spawned workplan. Residual is a *role*, +not a kind. Fleet list lives on State Hub, not in `SCOPE.md`. + +To create a new workplan: +1. Write the file following the format above +2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or + State Hub API is unavailable. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preimages/workplans/REINAH-WP-0008-simpler-metered-retry.md b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preimages/workplans/REINAH-WP-0008-simpler-metered-retry.md new file mode 100644 index 00000000..3603ca51 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preimages/workplans/REINAH-WP-0008-simpler-metered-retry.md @@ -0,0 +1,354 @@ +--- +id: REINAH-WP-0008 +type: workplan +title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal" +domain: infotech +repo: rein-aharness +status: blocked +owner: claude-code +topic_slug: rein-aharness +priority: high +created: "2026-10-13" +updated: "2026-10-16" +related: + - REINAH-WP-0003 +state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2" +--- + +# REINAH-WP-0008 — Simpler metered retry + +## Origin + +On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436` +(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected +runtime `ee3d0a53…` was built +(`docs/evidence/2026-10-13-corrected-owner-runtime-build.json`). The owner +authorized a new envelope and one retry. + +Preparing that retry took too many owner ceremonies: + +1. The runtime is built on the workstation and copied to railiance01 by hand. +2. `owner.json` and the spend policy hold five digests that the owner edits + by hand. +3. A failed attempt keeps its full hold even when no provider request was + made, so every failure needs a new envelope, ledger and pins. +4. Every attempt needs six native approvals. +5. The launcher bug that failed run `6efa9436` was found only by spending + money. A free check on the host would have caught it. + +The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record +the rest as decisions instead of blocking on them. + +**Goal:** a retry becomes: install the artifact, rehearse for free, approve +once, run. + +## A. Free rehearsal on the host + +```task +id: REINAH-WP-0008-T01 +status: done +priority: high +state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04" +``` + +Add a free rehearsal to the owner check. It runs everything +`metered-once --check` does, then executes the admitted runtime's +`rein-aharness` and `glas-harness` launchers inside bwrap from the mount +path, with no network, credentials, provider route or ledger write. The +sandbox profile is already resolved by `--check`. It prints one receipt with +the runtime digest and each launcher's exit code. + +Today's launcher check reads only the first line of each launcher. This +rehearsal runs them, the same way the failing run did. + +**Done when:** tests prove the rehearsal fails on a runtime whose launchers +point at a missing build directory (the `6efa9436` failure) and passes on a +relocated runtime. It must make no network calls and write nothing. The +owner procedure in `docs/owner-bootstrap.md` makes the rehearsal a required +step before any paid attempt. + +### Implemented — 2026-10-13 + +`rein-aharness metered-once --owner-config --rehearse` (the flag +implies `--check`, so it never reads a provider key or claims). The code is in +`rein_aharness/owner_rehearsal.py`. It uses sand-boxer's isolation flags and +read-only system binds, with the runtime read-only at `/opt/sandboxer/runtime`, +`--unshare-net` and `--clearenv`. It runs each Python launcher with `--help`, +plus `claude --version` when `build-info.json` declares Claude. A missing +bwrap fails closed. + +Tests (`tests/test_owner_rehearsal.py`, `tests/test_owner_bootstrap.py`): + +- With real bwrap, a relocated runtime passes. The uv trampoline that + re-executes a deleted build interpreter fails with 127, as run `6efa9436` + did. +- The rehearsal never claims and never reads the key. +- The command line has no writable bind and no credential. + +The corrected runtime `ee3d0a53…` rehearses clean: rein-aharness 0, +glas-harness 0, claude 0. + +## B. Release holds that provably cost nothing + +```task +id: REINAH-WP-0008-T02 +status: done +priority: high +state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9" +``` + +`SpendLedger.reconcile` keeps the full hold (`max(held, observed)`). Add an +evidence-backed release. If the run has zero provider-request reservations +and its request route is revoked, reconciliation may set the liability to +the observed amount. Otherwise the current rule stands. The release records +which evidence allowed it, and the CLI flag says so, for example +`--release-unused-hold`. + +This changes a deliberate safety rule, so it needs owner sign-off on the +exact rule before merge. That sign-off is recorded in T04's decision note. + +**Done when:** tests cover all four cases. A run with no request +reservations and a revoked route releases. A run with any request +reservation, or with a live route, refuses the release. A second, identical +reconcile changes nothing. The envelope is never marked breached by a +release. + +### Widened to observed-usage settlement — 2026-10-14 + +On 2026-10-13 the owner asked for three attempts within EUR 10 and chose +option B: settle to actual usage instead of releasing only unused holds. +Under full-hold accounting each request holds USD 4.64 (1M context plus +64k output), so EUR 10 cannot fit three attempts. + +Implemented as an opt-in envelope term (`settlement: observed-usage`) plus +`max_runs`; see `docs/owner-bootstrap.md`. Under it: + +- a completed request is charged its gateway-metered cost; +- runs and reconciliations are charged + `max(metered requests, reported or attested cost)`; +- unfinished requests keep their full hold, and overruns still freeze the + envelope. + +The run 6efa9436 shape (failure before any request) now reconciles to its +attested cost. Defaults and existing digests are unchanged. Tests: +`tests/test_spend_settlement.py`, plus renewal cases in +`tests/test_owner_renew.py`. + +Merge-ready, but the rule still needs the owner's acceptance in decision +`a93619a9`. + +## D. One owner command for renewal + +```task +id: REINAH-WP-0008-T03 +status: done +priority: high +state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2" +``` + +Add `rein-aharness owner renew --owner --runtime +--envelope `. It does three things: + +1. It refuses unless every reservation in the current ledger is settled. +2. It writes the new spend policy, initializes a fresh ledger, and updates + the runtime and spend-policy pins in `owner.json` together. Old files are + kept as backups. Existing ledgers are never reset. +3. It prints one summary for the owner to review: old to new runtime, old + to new policy digest, envelope cap and attempt limit, and ledger path. + +Add a dry-run mode that only prints the summary. Afterwards, run the T01 +rehearsal. + +**Done when:** a test renews from the 2026-09-27 layout to a new envelope. +The owner check passes on the result. A second renew with an unsettled +reservation refuses. No digest has to be typed by hand. + +### Implemented — 2026-10-13 + +The command is `rein-aharness owner renew --owner-config --policy --ledger +--runtime-path --envelope [--apply]`, in `rein_aharness/owner_renew.py`. +Without `--apply` it is a dry run. Choices: + +- **Envelope file:** only the envelope fields may change. Worker, target, + profile and grant pins carry over, and `SpendPolicy` validation runs on + the result. +- **Paths stay the same:** the old ledger is renamed to + `.retired-`, never reset, and the old policy and owner.json + are kept as `.bak`. Secrets Engine pins the `exec_owner` files by sha256 + and the ledger path through its fixed environment, so keeping the paths + means the catalog needs only new file hashes. The command prints them. +- **Refusals:** a held reservation, an open request route, an open SQLite + transaction, an owner.json that doesn't match the policy, a reused + envelope id, a disallowed field, or leftovers from an earlier renewal. + +Tests (`tests/test_owner_renew.py`): + +- A dry run changes nothing. +- An applied renewal passes the real owner check (`prepare`) at the + unchanged paths. +- A charged reservation, the railiance01 state after the 2026-10-13 + reconciliation, renews, and the row survives in the retired ledger. +- Bad envelopes and a second apply both refuse. + +Finding for T06: renewal still changes the pinned file hashes, so each +renewal needs a Secrets Engine catalog update and fresh approvals. + +## C. Envelopes that cover several attempts (decision) + +```task +id: REINAH-WP-0008-T04 +status: done +decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043" +priority: medium +state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612" +``` + +Owner decision: should future envelopes be admitted as "up to N attempts +within EUR X" instead of one attempt? The EUR 10 cap already fits two +USD 4.64 request holds. Record the choice, together with T02's sign-off, +as a decision note in `docs/`. T03's envelope format then reflects it. + +### Decided — 2026-10-14 + +The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and +observed-usage settlement, and resolved hub decision `a93619a9` with that +choice. Record: `docs/decisions/2026-10-14-spend-envelope-settlement.md`. +The terms are in spend memo `infd-20261014-b01`. + +## E. Ship the owner runtime as a container image (decision) + +```task +id: REINAH-WP-0008-T05 +status: wait +needs_human: true +decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5" +priority: low +state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee" +``` + +Decision for later, with Platform (railiance) and Sand-boxer: should the +owner runtime ship through the registry and GitOps, using the existing +`make image` path, instead of rsync and a manual digest check? Out of scope +here. If accepted, hand it off as a residual intake to the owning repos. + +## F. Approvals per envelope (decision) + +```task +id: REINAH-WP-0008-T06 +status: wait +needs_human: true +decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b" +priority: low +state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af" +``` + +Decision for later, with Secrets Engine: should one approval set cover an +envelope's attempts instead of six native approvals per attempt? The work +lives outside this repo. If accepted, hand it off as a residual intake. + +## Retry on the corrected runtime + +```task +id: REINAH-WP-0008-T07 +status: wait +needs_human: true +blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01" +priority: high +state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b" +``` + +Waits on T01 and T03. It does not need T02. + +Steps: + +1. Rebuild `ee3d0a53…`, or the then-current corrected runtime, from the + pinned commits. +2. Install it on railiance01. +3. Run the T01 rehearsal. +4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized + 2026-10-13). +5. Run exactly one attended attempt, which consumes fresh native approvals. + +Record the result in REINAH-WP-0003-T05/T06. + +### Waiting on the owner — 2026-10-14 + +Everything this repo can do is done: the rehearsal (T01), the settlement rule +(T02, T04) and renewal (T03) are in place. Draft memo `infd-20261014-b01` is +in informed-decision `ee6a65b`. What remains needs the owner, in order: + +1. Accept the memo in the attended sitting. +2. ~~Rebuild `ee3d0a53` from the pinned commits~~ (done 2026-10-15 as + `818b1b7c…`, see below) and install it on railiance01. +3. Run `metered-once --rehearse`. +4. Run `owner renew` with the memo's terms, then update the Secrets Engine + file pins. +5. Approve the native actions and run the first attempt. + +Sand-boxer's steps 2–4 in message `82faf25c` are this same sequence. + +### Runtime rebuilt — 2026-10-15 + +Step 1 is done. The owner approved a rebuild because `ee3d0a53…` was not kept +on the workstation. Sand-boxer built `818b1b7c…` from the same pinned inputs +and builder. The digest differs only because the build-source paths are +embedded in the artifact. The bwrap startup smoke passed (sandbox `28df7a1d`) +with no credentials and no model run. `818b1b7c…` is now the install +candidate. It is not installed on railiance01 (still `b6e4e8a4…`) or admitted. +Receipt: `docs/evidence/2026-10-15-rebuilt-owner-runtime.json`. The memo still +still has to be created, admitted, loaded and accepted first. The owner steps +are in informed-decision `docs/batches/2026-10-14-three-attempts/OPERATOR.md`. + +### Memo queued — 2026-10-15 + +Owner step 1 is in progress via the lifecycle intake path (owner's choice; the +review-record approval counts as spend authority). Workspace draft +`2ccb4bda-e318-51d2-a8dd-709db768494f`, review +`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`, queued by the owner in the State +Hub memo dispatcher; approval intent `accept` by `helixforge-factory`. Record: +informed-decision `d5f1021` (sitting.json, OPERATOR.md). The accepted approval id +becomes `authority_ref` for `owner renew`. Install candidate remains `818b1b7c`. + + +### Workload-only Glas source integration — 2026-10-16 + +Reviewed and integrated the one-line runtime-contract lock advance from Glas +`30be81808140a3a3d137e731d1fe9aed59ad8581` to published workload-only credential +delivery `7357115c8311e55ee3d495f5266f501a17ec3c89`. llm-connect `37436bb5` and +sandboxer `221126cb` remain pinned. This is source integration under T07, not +native artifact installation or admission. T07 remains waiting with its existing +owner gates. + +Prior preparation used rein base `be5ef400` plus isolated lock-only commit +`c811fc43` to build candidate +`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`. The +candidate's installed packages passed actual SandboxManager/Glas gateway proof +with unchanged `harness.agent-dev-local@1.1.1` (still unverified): real rein/Glas +CLI and Claude 2.1.266 startup, read-only runtime, synthetic host-file and +inherited environment/child-process denial, exact task identity/private state, +deterministic workspace Git effect, task removal and destruction. A separately +labelled synthetic route fixture passed workload-only delivery, output redaction +and clean subsequent housekeeping through the same actual manager. Wrong +actor/project/run, expired execution, expiry cleanup, cancellation and +post-cancellation refusal passed. All 141 Glas tests and seven rein lock/rehearsal +tests passed; whole artifact digest remained unchanged after proof. + +The first proof runner correctly encountered the manager's PermissionError for +wrong consumer but expected ValueError/RuntimeError. Its finally cleanup ran; +the runner catch was refined and the failure retained. This does not earn an +unchanged execution success. No model/provider request, real credential read, +queue access or spend occurred. + +The preparation artifact is not the artifact of this integrated owner revision. +Rebuild from the exact committed owner source and verify its new digest and +actual-manager proof before considering installation. Source inputs, receipts, +reproducible proof and attempt history are retained in the +[Custodian evidence directory](../../the-custodian/docs/changes/netkingdom-target-2026-10-16/agent-isolation-next/README.md). +The same directory records the integrated-source rebuild separately. + +Retain `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their outcomes as historical +evidence. Native acceptance still requires the queued spend memo lifecycle, +owner artifact installation/rehearsal, renewal and Secrets Engine file-pin +updates, required native approvals and bounded real-model/tool proof. Actual +interactive assignment migration and external tool-path denial remain Custodian +acceptance obligations; no profile readiness or supervision authority changed. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preparation.json b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preparation.json new file mode 100644 index 00000000..e8847904 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/preparation.json @@ -0,0 +1,11 @@ +{ + "observed_at": "2026-10-17T06:55:02.225819+00:00", + "head": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb", + "status_before": "", + "initial_review_manifest_sha256": "sha256:cae64cd2c52623dadc1905f0f418a91c99ab1eb981e02accc28855fe22c43f28", + "index_sha256": "sha256:08c186d73338c3eb03a8216c7025fdb6cb386a647802f4df7f096c2f177ff98c", + "all_8_uuids_preserved": true, + "all_7_status_human_decision_and_other_task_fields_preserved": true, + "canonical_write_pending": true, + "native_proposal_compatibility": "Original native-install R1/R2 and command helpers remain immutable. Stable REINAH-prefixed proposal ID is an opaque trial ID and is not renamed; actual canonical owner task is clarified as HARNESS-WP-0008-T07 with identical UUID80318842-ccaa-5484-a54a-3d9981eaec8b. This is no action revision, scope change or authority." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/rein-aharness-source-correction.patch b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/rein-aharness-source-correction.patch new file mode 100644 index 00000000..c80cda85 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/rein-aharness-source-correction.patch @@ -0,0 +1,116 @@ +--- a/AGENTS.md ++++ b/AGENTS.md +@@ -11,8 +11,10 @@ + + `REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from + before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through +-`HARNESS-WP-0003` and `HARNESS-WP-ADHOC-2026-09-04` are older ids from the +-agent-harness name. Preserve all of these in references and archives, but use ++`HARNESS-WP-0003`, `HARNESS-WP-0008` and ++`HARNESS-WP-ADHOC-2026-09-04` are preserved historical ids. Workplan 0008 ++keeps its originally issued HARNESS identity after the 2026-10-17 correction ++of an erroneous REINAH relabel; its existing REINAH filename is only a path. Preserve all of these in references and archives, but use + the hub-registered `REINAH-WP-` prefix for every new workplan and task. + + --- +--- a/workplans/REINAH-WP-0008-simpler-metered-retry.md ++++ b/workplans/REINAH-WP-0008-simpler-metered-retry.md +@@ -1,5 +1,5 @@ + --- +-id: REINAH-WP-0008 ++id: HARNESS-WP-0008 + type: workplan + title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal" + domain: infotech +@@ -9,15 +9,26 @@ + topic_slug: rein-aharness + priority: high + created: "2026-10-13" +-updated: "2026-10-16" ++updated: "2026-10-17" + related: + - REINAH-WP-0003 + state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2" + --- + +-# REINAH-WP-0008 — Simpler metered retry ++# HARNESS-WP-0008 — Simpler metered retry + + ## Origin ++ ++Identity correction, 2026-10-17: restore the originally issued ++`HARNESS-WP-0008` and T01–T07 canonical IDs, retaining every existing Hub UUID, ++status, decision and human/native gate. Commit `a85de661` briefly relabelled ++this workplan `REINAH-WP-0008` while preserving HARNESS-derived UUIDs in response ++to a C-26 warning. Canon grandfathering preserves existing IDs; C-26 applies ++to new plans and supplies no record-rekey authority. The REINAH filename remains ++as an existing backing path, and the erroneous label remains historical ++provenance rather than another canonical record. Future new workplans still ++use REINAH-WP. No UUID migration, retirement, human disposition, installation, ++spend authority or native acceptance is granted by this source correction. + + On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436` + (`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected +@@ -45,7 +56,7 @@ + ## A. Free rehearsal on the host + + ```task +-id: REINAH-WP-0008-T01 ++id: HARNESS-WP-0008-T01 + status: done + priority: high + state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04" +@@ -91,7 +102,7 @@ + ## B. Release holds that provably cost nothing + + ```task +-id: REINAH-WP-0008-T02 ++id: HARNESS-WP-0008-T02 + status: done + priority: high + state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9" +@@ -140,7 +151,7 @@ + ## D. One owner command for renewal + + ```task +-id: REINAH-WP-0008-T03 ++id: HARNESS-WP-0008-T03 + status: done + priority: high + state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2" +@@ -196,7 +207,7 @@ + ## C. Envelopes that cover several attempts (decision) + + ```task +-id: REINAH-WP-0008-T04 ++id: HARNESS-WP-0008-T04 + status: done + decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043" + priority: medium +@@ -218,7 +229,7 @@ + ## E. Ship the owner runtime as a container image (decision) + + ```task +-id: REINAH-WP-0008-T05 ++id: HARNESS-WP-0008-T05 + status: wait + needs_human: true + decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5" +@@ -234,7 +245,7 @@ + ## F. Approvals per envelope (decision) + + ```task +-id: REINAH-WP-0008-T06 ++id: HARNESS-WP-0008-T06 + status: wait + needs_human: true + decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b" +@@ -249,7 +260,7 @@ + ## Retry on the corrected runtime + + ```task +-id: REINAH-WP-0008-T07 ++id: HARNESS-WP-0008-T07 + status: wait + needs_human: true + blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01" diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-current-driver-reference.patch b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-current-driver-reference.patch new file mode 100644 index 00000000..80c1e8f1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-current-driver-reference.patch @@ -0,0 +1,11 @@ +--- a/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md ++++ b/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md +@@ -89,7 +89,7 @@ + id: SAND-WP-0015-T04 + status: wait + blocked_on: message-from:rein-aharness +-blocking_reason: "Tracked as REINAH-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt." ++blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt." + priority: high + state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93" + ``` diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-driver-reference.patch b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-driver-reference.patch new file mode 100644 index 00000000..302dfbe1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-driver-reference.patch @@ -0,0 +1,33 @@ +--- a/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md ++++ b/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md +@@ -89,7 +89,7 @@ + id: SAND-WP-0015-T04 + status: wait + blocked_on: message-from:rein-aharness +-blocking_reason: "Tracked as REINAH-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt." ++blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt." + priority: high + state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93" + ``` +@@ -354,12 +354,12 @@ + T04 stays `wait`; the plan stays `blocked`. + + 2026-10-14 check: rein-aharness pushed `9a4469d` (corrected build record) and +-opened REINAH-WP-0008 (simpler metered retry, active). No railiance01 install/ ++opened HARNESS-WP-0008 (simpler metered retry, active). No railiance01 install/ + admission of `ee3d0a53` or successful acceptance receipt yet; request sent on + hub message `82faf25c`. T04 stays `wait`; plan stays `blocked`. + + 2026-10-14 rein-aharness reply (thread `82faf25c`): step 1 done (`9a4469d` on +-origin/main). Steps 2-4 are REINAH-WP-0008-T07. Budget is now up to three ++origin/main). Steps 2-4 are HARNESS-WP-0008-T07. Budget is now up to three + attempts within EUR 10 under memo `infd-20261014-b01` (observed-usage + settlement); order: memo acceptance in the attended informed-decision sitting, + rebuild and install `ee3d0a53` on railiance01, `metered-once --rehearse`, +@@ -371,5 +371,5 @@ + absolute source paths (`direct_url.json`, `build-info.json`). Owner-entrypoint + startup smoke passed in sandbox `28df7a1d`; no credentials or model calls. + Sent to rein-aharness (message `8a7962f7`, thread `82faf25c`) to record as the +-then-current corrected runtime for REINAH-WP-0008-T07 and to load the unsigned ++then-current corrected runtime for HARNESS-WP-0008-T07 and to load the unsigned + memo sitting. Nothing installed on railiance01. T04 stays `wait`. diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-driver-reference.preimage.sha256 b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-driver-reference.preimage.sha256 new file mode 100644 index 00000000..4f85292f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/sand-boxer-driver-reference.preimage.sha256 @@ -0,0 +1 @@ +sha256:230d1ef37aa9c2023335a617492a72678c4671a8bc82d5577e9d7a7cea6e8fe4 diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/verification.json b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/verification.json new file mode 100644 index 00000000..32e0e383 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-followthrough-r1/verification.json @@ -0,0 +1,99 @@ +{ + "observed_at": "2026-10-17T06:55:41.814838+00:00", + "status": "authorized_source_identity_restored_not_published", + "source_changed_paths": [ + { + "path": "AGENTS.md", + "before_sha256": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034", + "after_sha256": "sha256:da71c6e3d76d7c867b2db6e67cd919fba594acc3804d3784dd744438103560c2" + }, + { + "path": "workplans/REINAH-WP-0008-simpler-metered-retry.md", + "before_sha256": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68", + "after_sha256": "sha256:1118924bb159f8a52a37ace8b8663c75274b89928f7a8b957638428af224b054" + } + ], + "head_unchanged": true, + "index_unchanged": true, + "source_uuid_identity_consistent": true, + "all_hub_rows_and_two_dependency_rows_unchanged": true, + "task_bindings": [ + { + "canonical_source_id": "HARNESS-WP-0008-T01", + "uuid": "c6536c8d-7862-5de6-ad2b-401077b60c04", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "done", + "needs_human": false, + "record_id": null + }, + { + "canonical_source_id": "HARNESS-WP-0008-T02", + "uuid": "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "done", + "needs_human": false, + "record_id": null + }, + { + "canonical_source_id": "HARNESS-WP-0008-T03", + "uuid": "87457216-2371-51ca-b3d4-3917e6f180a2", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "done", + "needs_human": false, + "record_id": null + }, + { + "canonical_source_id": "HARNESS-WP-0008-T04", + "uuid": "898e02af-51bb-5e8d-93d0-ae908466c612", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "done", + "needs_human": false, + "record_id": null + }, + { + "canonical_source_id": "HARNESS-WP-0008-T05", + "uuid": "b486e86b-9d0f-501d-a394-771b40f771ee", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "wait", + "needs_human": true, + "record_id": null + }, + { + "canonical_source_id": "HARNESS-WP-0008-T06", + "uuid": "4cf721e6-724b-5c64-91bb-67b098bb97af", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "wait", + "needs_human": true, + "record_id": null + }, + { + "canonical_source_id": "HARNESS-WP-0008-T07", + "uuid": "80318842-ccaa-5484-a54a-3d9981eaec8b", + "source_derived_uuid_matches_existing": true, + "hub_unchanged": true, + "status": "wait", + "needs_human": true, + "record_id": null + } + ], + "no_hub_write": true, + "no_retirement_acknowledged": true, + "no_uuid_migration": true, + "no_sync_or_publish": true, + "driver_patches_only": true, + "native_proposals_and_helpers_unchanged": true, + "initial_review_history": "Initial review recommended separately revising native-install references; root clarified opaque stable proposal ID and pending R2 action stay immutable. The current companion mapping supersedes that recommendation without changing R1 review bytes.", + "owner_task_mapping": { + "old_erroneous_source_label": "REINAH-WP-0008-T07", + "actual_canonical_owner_task": "HARNESS-WP-0008-T07", + "unchanged_uuid": "80318842-ccaa-5484-a54a-3d9981eaec8b", + "native_action_scope_or_revision_changed": false + }, + "next_step": "Root reviews two current driver-reference patches, then normal owner preparation/source publication and receiving without retirement acknowledgement; confirm 7 null record_ids converge on HARNESS IDs with no UUID/status/human-gate change." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-references.json b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-references.json new file mode 100644 index 00000000..0b0ff07a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/identity-binding-review-r1/source-references.json @@ -0,0 +1,63 @@ +{ + "status": "source_metadata_only", + "references": [ + { + "repo": "rein-aharness", + "path": "AGENTS.md", + "head_observed": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb", + "sha256": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034" + }, + { + "repo": "rein-aharness", + "path": "workplans/REINAH-WP-0008-simpler-metered-retry.md", + "head_observed": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb", + "sha256": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68" + }, + { + "repo": "repo-manager", + "path": "src/repo_manager/identifiers.py", + "head_observed": "3bcb1e1cf3e58b263f690534f7f28024d75cdbf6", + "sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a" + }, + { + "repo": "repo-manager", + "path": "docs/work-record-uuid-derivation_v1.md", + "head_observed": "3bcb1e1cf3e58b263f690534f7f28024d75cdbf6", + "sha256": "sha256:600f5c16a0f9325df56dd698311d0151571742782664a2d2bc791b8f5d964157" + }, + { + "repo": "state-hub", + "path": "api/services/forge_projection.py", + "head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a", + "sha256": "sha256:b980e62cbd85c3e7df42b01abf14354e04133374db94af6531dcd91629c3487d" + }, + { + "repo": "state-hub", + "path": "api/services/work_record_identifier_migration.py", + "head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a", + "sha256": "sha256:f02fc379d03b2ae78e2b2ed781430d45e025221fd55558be3df60105046ecf53" + }, + { + "repo": "state-hub", + "path": "api/models/work_record_identifier_alias.py", + "head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a", + "sha256": "sha256:101e6013d52e5aeb66a44c8b666ff40cdbcd9e52028149c7b891d801039c71e5" + }, + { + "repo": "state-hub", + "path": "tests/test_forge_projection.py", + "head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a", + "sha256": "sha256:3e41a2ba7b68dd86ec6d31984063d0d7846b41ec40a4545f9fcf9582c20cf9d2" + }, + { + "repo": "the-custodian", + "path": "canon/standards/work-record-types_v0.1.md", + "sha256": "sha256:ab5d9de07fd899fc5334c9912571ed3d6748f2ee4bd0c9f81c5606c4727024d1" + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/native-gates-r2/rein-aharness-receiving.json", + "sha256": "sha256:583561db0386f95650125045f786b96370d7743e132d24cf50342a56208333fd" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/inspect_public_image.py b/docs/changes/netkingdom-target-2026-10-17/inspect_public_image.py new file mode 100644 index 00000000..1ed1b231 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/inspect_public_image.py @@ -0,0 +1,103 @@ +"""Hash selected public image files without pulling, installing or running it.""" +from pathlib import Path, PurePosixPath +import hashlib +import io +import json +import re +import tarfile +import time +import urllib.parse +import urllib.request + +BASE = Path(__file__).resolve().parent +HOST = 'https://forgejo.coulomb.social' +IMAGE = '/v2/coulomb/state-hub/' +DIGEST = 'sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015' +DEADLINE = time.monotonic() + 180 +class NoRedirect(urllib.request.HTTPRedirectHandler): + def redirect_request(self, *args, **kwargs): + raise RuntimeError('redirect_refused') +opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect) +def get(path, limit, token=None, accept=None): + assert time.monotonic() < DEADLINE + headers = {} + if token: + headers['Authorization'] = 'Bearer ' + token + if accept: + headers['Accept'] = accept + with opener.open(urllib.request.Request(HOST + path, headers=headers), timeout=30) as response: + raw = response.read(limit + 1) + assert len(raw) <= limit + return raw +query = urllib.parse.urlencode({'service': 'container_registry', 'scope': 'repository:coulomb/state-hub:pull'}) +auth = json.loads(get('/v2/token?' + query, 65536)) +token = auth.get('token', auth.get('access_token')) +assert isinstance(token, str) and 0 < len(token) < 60000 +raw = get(IMAGE + 'manifests/' + DIGEST, 1048576, token, + 'application/vnd.docker.distribution.manifest.v2+json') +assert 'sha256:' + hashlib.sha256(raw).hexdigest() == DIGEST +manifest = json.loads(raw) +assert manifest['schemaVersion'] == 2 and len(manifest['layers']) <= 80 +assert sum(x['size'] for x in manifest['layers']) <= 500 * 1024**2 +files, packages, layers = {}, {}, [] +for layer in manifest['layers']: + pin = layer['digest'] + assert re.fullmatch('sha256:[0-9a-f]{64}', pin) and 0 <= layer['size'] <= 160 * 1024**2 + blob = get(IMAGE + 'blobs/' + pin, layer['size'], token) + assert len(blob) == layer['size'] and 'sha256:' + hashlib.sha256(blob).hexdigest() == pin + with tarfile.open(fileobj=io.BytesIO(blob), mode='r|*') as archive: + count = 0 + for entry in archive: + count += 1 + assert count <= 100000 and time.monotonic() < DEADLINE + path = entry.name.removeprefix('./') + assert not path.startswith('/') and '..' not in PurePosixPath(path).parts + parent, _, basename = path.rpartition('/') + # Apply OCI deletions to the selected metadata view. + if basename.startswith('.wh.'): + erased = parent + '/' + basename[4:] + for key in list(files): + if key == erased or key.startswith(erased + '/'): + del files[key] + for key in list(packages): + if key == erased or key.startswith(erased + '/'): + del packages[key] + continue + code = (path == 'app/api/services/historical_projection.py' + or path.startswith('app/migrations/versions/') and path.endswith('.py') + or '/site-packages/hub_core/' in path and path.endswith('.py') + or path.startswith('tmp/hub-core/hub_core/') and path.endswith('.py') + or path == 'tmp/.ci-build-id') + metadata = '/site-packages/' in path and path.endswith('.dist-info/METADATA') + if not code and not metadata: + continue + assert entry.isfile() and entry.size <= 2 * 1024**2 + stream = archive.extractfile(entry) + content = stream.read(2 * 1024**2 + 1) + assert len(content) == entry.size + if code: + files[path] = 'sha256:' + hashlib.sha256(content).hexdigest() + if metadata: + text = content.decode('utf-8') + name = re.findall(r'^Name: ([A-Za-z0-9_.-]+)$', text, re.M) + version = re.findall(r'^Version: ([A-Za-z0-9_.+!-]+)$', text, re.M) + assert len(name) == len(version) == 1 + packages[path] = {'name': name[0], 'version': version[0], + 'metadata_sha256': 'sha256:' + hashlib.sha256(content).hexdigest()} + layers.append({'digest': pin, 'compressed_bytes': len(blob), 'archive_entries': count}) + del blob +expected = 'sha256:' + hashlib.sha256(Path('/home/worsch/state-hub/api/services/historical_projection.py').read_bytes()).hexdigest() +assert files.get('app/api/services/historical_projection.py') == expected +assert files.get('tmp/.ci-build-id') == 'sha256:' + hashlib.sha256(b'51bc70d\n').hexdigest() +migration = 'app/migrations/versions/c4d5e6f7a8b9_inbox_feed_revision.py' +assert files.get(migration) == 'sha256:48f1730037be99b5623e878d7f20a06bec243ce27317fde81c82e16006d7b3ce' +del auth, token +result = {'schema': 'statehub.public-image-inspection.v1', 'status': 'verified_selected_image_files', + 'source_revision': '51bc70d1ff8f25450a8650d8e50caf053093a02e', 'image_digest': DIGEST, + 'anonymous_protocol_only': True, 'token_retained': False, 'image_executed': False, + 'image_installed_or_daemon_pulled': False, 'layers': layers, 'files': files, + 'python_package_metadata': packages, + 'limitations': ['Selected code and package metadata inventory; no full application acceptance or hermetic build provenance.', + 'Floating dependency origins remain unknown; observed bytes and versions are bound to this immutable image digest.']} +(BASE / 'statehub-public-image-inspection.json').write_text(json.dumps(result, indent=2) + '\n') +print(json.dumps({'status': result['status'], 'layers': len(layers), 'selected_files': len(files), 'packages': len(packages)})) diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/KEY39.preimage.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/KEY39.preimage.md new file mode 100644 index 00000000..b22aa6a1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/KEY39.preimage.md @@ -0,0 +1,368 @@ +--- +id: KEY-WP-0039 +type: workplan +title: "Negative-check tokens for the secrets-engine OpenBao JWT role" +domain: infotech +repo: key-cape +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-10" +updated: "2026-10-17" +depends_on: [] +related_repos: + - railiance-platform + - secrets-engine +state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86" +--- + +# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role + +## Why + +CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role +`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong +`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT +and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine +`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked +whether KeyCape can issue the negative tokens without touching the +`secrets-engine` registration. + +KeyCape signs only what a registration declares. It has no way to mint a token +with arbitrary claims, and adding one would create a forging path in the issuer. +So the negative tokens must come from real, separate registrations that differ +from `secrets-engine` in exactly one property each. + +## Boundaries + +- The `secrets-engine` registration is not changed. +- Probe clients are confidential `client_credentials` clients with the minimum + 60-second lifetime, scopes that grant nothing, and secrets minted into custody + like any other client. They are removed after the proof. +- The probe script prints only accept or refuse booleans per case, never tokens or + secrets. It runs attended. + +## T01 — Define the probe registrations + +```task +id: KEY-WP-0039-T01 +status: done +priority: medium +state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" +``` + +Write a non-secret request packet under `docs/` with one probe client per claim +mismatch, each identical to `secrets-engine` except for: + +- `aud`: a different access-token resource audience +- `sub`: a different service subject +- `tenant`: a tenant other than `platform` +- `roles`: a role other than `secrets-engine` +- `scope`: a scope outside the role's bound scope + +Two cases need no probe client. A service token always carries +`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong +`principal_type` case uses a human access token from an attended login. The +coding-agent case uses the existing `codex-railiance-platform` registration (role +`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live; +T01 confirms that. + +The expired-JWT case reuses any probe client: request a token, wait past its +60-second lifetime, then present it. + +Acceptance: railiance-platform agrees the packet covers every refusal that +CCR-2026-0038 names. + +2026-10-10: railiance-platform agreed that the case list covers every +CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no +provider fallback) are theirs and secrets-engine's. Packet written at +`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two +ways, both sent back for re-agreement: + +- **Expired case.** A token from another probe already fails a claim, so its + refusal does not show that expiry was checked. It needs a sixth client, + `openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That + makes it a second credential able to log in as secrets-engine for the session. + OpenBao's `expiration_leeway` (150s by default) also extends the wait. +- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is + set. Every probe except the aud probe therefore pins + `audience: secrets-engine-openbao`, so it fails only on its own claim. + +Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's +environment, because KeyCape resolves `secretRef` at startup. The packet uses a +transient Kubernetes Secret that is deleted in the same session. + +## T02 — Register the probe clients, run the attended probe, then remove them + +```task +id: KEY-WP-0039-T02 +status: wait +depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved." +priority: medium +state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" +``` + +2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md. + +Write the attended script, which prints booleans only. + +2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in +`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused +boolean per case plus the refusal class (`claim_mismatch`, `expired`, +`not_service_principal`), and no token, secret, claim value or OpenBao error text. +A transport failure or an unexpected response is reported as `inconclusive`, never +as a refusal. A token that OpenBao accepts is revoked at once and fails the run. +Still waiting: railiance-platform's re-agreement on the two departures above, and +an attended registry session with net-kingdom. Register the clients +through the normal registration path, hand the run to railiance-platform, record +a value-free receipt in `docs/evidence/`, and deregister the clients afterwards. + +Acceptance: every case is refused, the receipt is committed, and the probe clients +are gone. + + +2026-10-11 source preparation: T01 remains progress because the exact expiry +client and audience-pin amendments require platform-owner re-agreement; the +prior agreement covered the earlier case list, not this revised packet. The +prepared runner revision adds a valid expiry positive control and immediate +revoke, exact decoded claim-isolation preflight (not signature evidence), +semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen +local tests pass; original-source semantic regressions demonstrate two false +successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner +returns, live authority, effective leeway, custody boundaries and cleanup. No +live token, registry or OpenBao action occurred. T02 continues to wait for owner +re-agreement plus separate attended live authorization. MASON-WP-0009-T05 +operator/ESO capability and nonmember checks remain independent acceptance. + +2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans. + + +2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner +acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md) +confirms all 14 executable/test/producer-decision pins and three Platform source +pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing +source acceptance remains valid. Resolved registration go caafe4be covers only +the original secrets-engine-openbao client (KEY-WP-0037-T03, deployed 6f79a2af, +NK-WP-0046 exact-match helper); it does not authorize new probe clients or +coding-client custody, and no renewed original-client approval is requested. +The standing attended rule remains separate, for reviewed commands within +existing scope; no new protocol or blanket approval request is introduced. The +remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof, +admission of the existing coding-client proposal with sanitized owner readiness +returns, then one attended negative/revoke/cleanup receipt committed with probe +clients removed. No native acceptance or credential/registration action is +inferred from this coordinator review. + + +## KeyCape issuer-source compatibility inspection for lifecycle v2 + +```task +id: KEY-WP-0039-T03 +status: done +priority: medium +state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73" +``` + +2026-10-11: Completed KeyCape's own issuer-source inspection for +KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256 +6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed. +Read AGENTS.md, governing INTENT.md, current client-credentials claim source, +registration fragments, native caller-authentication commands and frozen v2 +issuer transport/probe wiring. Seventeen unchanged runner tests pass locally. +This task completes only issuer-source inspection; the resulting narrow +source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment). +T01 stays done and T02 stays open for actual native registration, token input, +refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner +checkout was changed during this source inspection. Owner source files were +read for the bounded issuer compatibility check. + +Current token.go:306-309 returns HTTP 401 invalid_profile_usage with +feature=client_id for a removed confidential client; wrong-secret +feature=Authorization cannot count as removed-registration evidence. The +proposal and packet comments are narrowly corrected against that current +issuer contract. Prior T01/source re-agreement history is preserved; this source +acknowledgment supplies no NetKingdom runtime-owner review or founder live go. + +The private token-producer handoff remains a real execution gap under T02. +Existing keycape login/service-token commands require a new private output +file; login also needs a concrete admitted public PKCE client/callback and +attended browser/MFA. The lifecycle expects an in-memory private stdin object +containing human_token and coding_agent_token, but no reviewed file-to-process +or process-to-process producer, admitted human client/callback, custody-injected +coding-agent source, native placement or producer cleanup receipt has been +provided. No unattended token producer or admitted secret route is invented. +Resolve those exact owner contracts before founder native approval; existing +static registration is not live token custody or producer admission. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed +KeyCape issuer-facing inspection before the new formal acknowledgment in +docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4 +manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8, +executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d. +NetKingdom's independent V4 source review is approved, receipt SHA256 +4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb. +The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain +retained as one correction trial; they are not extra scored successes. + +V4 retains the unchanged issuer runner and six-client claim design, and fixes +finite/nonboolean windows, true total request deadlines, late-known OpenBao +session private revoke/refusal and guarded bounded final Secret deletion. +The issuer-facing acknowledgement covers only stopping late token exchanges, +claim source/shape and preserving token cleanup failure/unknown outcomes; it +does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41 +supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass +locally. No native tokens, credentials, issuer mutations or live cleanup were +performed. T01 stays done; T02 stays open and the genuine private token-producer +handoff remains its next source prerequisite before actual founder native go. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed +KeyCape's own current issuer-source/claim/cleanup-contract inspection before +any new formal acknowledgment. Current token.go:306-309 still distinguishes +removed-client invalid_profile_usage feature=client_id from wrong-secret +feature=Authorization. The six-client design, audience source and expiry +positive control remain unchanged, and the original runner's 17 tests pass. +Exact V5 manifest SHA256 +d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285; +executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360. +V5 has 46 passing supplied lifecycle checks. It adds a single exceptional +response-close abort under a fixed20ms cap clipped to nonresettable aggregate +cleanup, and to positive original request time remaining, with expired aggregate +zero calls, fixed private errors and truthful known-session revoke/unknown +outcomes. This inspection covers issuer-facing token lifetime and claim +interfaces only; NetKingdom and Platform independently own their runtime and +backend review decisions. Previous V2/V3 Net source refusals and Platform V4 +exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are +historical narrow source inspections, never aggregate native approval. +T01 remains done, T02 open. No native actions or credentials occurred. The real +private token-producer/client/callback/coding-agent custody/cleanup handoff gap +stays the next prerequisite; no unattended admission is inferred. + +Exact independent V5 source decisions were then verified: NetKingdom approved +receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc +(46 supplied +17 runner +21 independent checks); Platform approved receipt +SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0 +(46 supplied +5 independent +17 previously verified runner checks). Those are +foreign owners' own source decisions, not KeyCape runtime/backend reviews or +founder native authority. Formal KeyCape bounded issuer acknowledgment follows +in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json. + + +2026-10-11 T02 continuing source preparation: private producer decision packet +`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI +flows, selected existing netkingdom-bootstrap-console literal-loopback human +client subject to owner purpose/MFA/listener admission, and the existing +codex-railiance-platform source identity subject to actual live/custody/consumer +admission. No existing coding-agent delivery receipt was found; AppRole tokens +do not substitute for issuer JWTs. A bounded pipe-only adapter for the current +CLI's two caller-owned private files has 18 passing synthetic checks, with +identity-guarded explicit file consumption and fixed value-free failure output. +Decoded claim shape does not prove signatures or authenticated admission. +See docs/openbao-negative-token-producer-decision.md and .json for exact owner +choices and missing receipts. The exact trusted workstation-to-node launcher, +private-file allowance and railiance01 Bao route/public-health guard still need +source review and founder disposition before any token/temporary credential/ +mutation/restart. No registration, custody, login, native action or credential +request occurred. V5 and failed review histories retained; T01 stays done and +T02 open. No new work record or native acceptance is inferred. + + +2026-10-11 same T02 source-preparation correction for +KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer +refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink). +V2 has source-pinned private launch/ready handshake compatible with exact +Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before +ANY caller-token file load/consume/write; separate local data/control pipes +reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget +only clips downward after readiness. 31 distinct producer/launcher synthetic +checks and 74 compatible consumer supplied checks pass. No SSH/native/login or +credential access was performed. Stat/unlink remains non-atomic and requires +continuously exclusive sameUID custody; the retained race test is a custody +violation counterexample, not a claim of repaired filesystem atomicity. Receipt +keeps atomic delete/copy cleanup unverified and possible copies true. Exact +trusted launcher/directSSH profile/private tmpfs allowance, selected human +client/MFA purpose reuse and coding-agent registration/custody/delivery remain +unadmitted owner choices. Native node route, role/leeway/window/founder decision +and actual negative/cleanup acceptance remain pending. T01done and T02open; no +new task or plan, and V1/V5-V8/current failure histories are not overwritten. + + +2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair +source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty +now propagates into sticky remote consumer uncertainty before launcher early +return; local SSH termination never proves remote no-effects/recovery. Actual +full-payload then interrupted confirmation and4096-byte partialpipe failures +are retained regressions. 33 distinct source checks pass. Exact Net V9 source +bytes unchanged; new source reviews required, not implicit repin. Native +client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session +authority remain unadmitted. T01done/T02open; no new task/plan or native action. + + +2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal +NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded +nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation +budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not +a renewed launcher allowance. 40 own source tests plus9 retained paired +wrapper/child handshake regressions pass with synthetic/no native transport. +V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact +bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/ +maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/ +credentials/native/owner checkout changes; no task/plan duplication. T01done +and T02open; source correction is not native acceptance. + + +2026-10-11 T02 source-design disposition checkpoint: founder accepted exact +source memo `infd-20261011-key39-source-design-r1`, review +`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`. +Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval +`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject +`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL: +`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`. +Root's authenticated confirmed readback is +`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`, +SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed +`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256 +`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256 +`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction. + +Acceptance covers recommended source preparation for existing bootstrap-console +human-client purpose reuse, returning existing coding-agent custody admission or +preparing a narrow owner proposal, proposed founder directSSH source profile, +and conditional exclusive-owner tmpfs files. It does not establish actual +registration/MFA/listener, coding-agent custody/delivery, transport/file admission, +node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh +attended native go. The published choice documents remain historical exact +pending proposals; this appended receipt records their later source disposition. +T02 remains wait on external owner custody/admission and separate attended +execution authorization. Source acceptance permits further source preparation; +actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03 +remain done. No native or credential action is authorized by this source receipt. +See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact +owner returns and safe metadata evidence needed before preparing a runnable +attended execution proposal. + +2026-10-11 ordered-goal qualification (T02 only): the final source runner already +exists and its 17 local semantic tests pass. No new helper or source protocol is +needed to repeat accepted preparation. Native execution follows the first +Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03); +only those task metadata were checked, not wait-task bodies. NetKingdom owns the +registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns +leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is +already recorded resolved; do not ask again. Attended human authentication and +owner-held existing coding-agent custody go on the single attended checklist. +No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred. + +2026-10-17 Platform owner returns (source-prepared, not admitted): +railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md` +(commit `0a9617a`). The coding client `codex-railiance-platform` has no +admission, custody or delivery record. The existing narrow proposal +`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`) +remains the one to admit, so the coding-agent case stays unrunnable until owners +admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches +the packet's claim design. It declares no `expiration_leeway`, so the expiry +probe must assume OpenBao's 150s default until an attended sanitized read +confirms the live value. The node-local Bao origin is not declared in source and +also needs that attended read. T02 stays wait. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/KEY39.proposed.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/KEY39.proposed.md new file mode 100644 index 00000000..4398517a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/KEY39.proposed.md @@ -0,0 +1,368 @@ +--- +id: KEY-WP-0039 +type: workplan +title: "Negative-check tokens for the secrets-engine OpenBao JWT role" +domain: infotech +repo: key-cape +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-10" +updated: "2026-10-17" +depends_on: [] +related_repos: + - railiance-platform + - secrets-engine +state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86" +--- + +# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role + +## Why + +CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role +`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong +`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT +and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine +`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked +whether KeyCape can issue the negative tokens without touching the +`secrets-engine` registration. + +KeyCape signs only what a registration declares. It has no way to mint a token +with arbitrary claims, and adding one would create a forging path in the issuer. +So the negative tokens must come from real, separate registrations that differ +from `secrets-engine` in exactly one property each. + +## Boundaries + +- The `secrets-engine` registration is not changed. +- Probe clients are confidential `client_credentials` clients with the minimum + 60-second lifetime, scopes that grant nothing, and secrets minted into custody + like any other client. They are removed after the proof. +- The probe script prints only accept or refuse booleans per case, never tokens or + secrets. It runs attended. + +## T01 — Define the probe registrations + +```task +id: KEY-WP-0039-T01 +status: done +priority: medium +state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8" +``` + +Write a non-secret request packet under `docs/` with one probe client per claim +mismatch, each identical to `secrets-engine` except for: + +- `aud`: a different access-token resource audience +- `sub`: a different service subject +- `tenant`: a tenant other than `platform` +- `roles`: a role other than `secrets-engine` +- `scope`: a scope outside the role's bound scope + +Two cases need no probe client. A service token always carries +`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong +`principal_type` case uses a human access token from an attended login. The +coding-agent case uses the existing `codex-railiance-platform` registration (role +`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live; +T01 confirms that. + +The expired-JWT case reuses any probe client: request a token, wait past its +60-second lifetime, then present it. + +Acceptance: railiance-platform agrees the packet covers every refusal that +CCR-2026-0038 names. + +2026-10-10: railiance-platform agreed that the case list covers every +CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no +provider fallback) are theirs and secrets-engine's. Packet written at +`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two +ways, both sent back for re-agreement: + +- **Expired case.** A token from another probe already fails a claim, so its + refusal does not show that expiry was checked. It needs a sixth client, + `openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That + makes it a second credential able to log in as secrets-engine for the session. + OpenBao's `expiration_leeway` (150s by default) also extends the wait. +- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is + set. Every probe except the aud probe therefore pins + `audience: secrets-engine-openbao`, so it fails only on its own claim. + +Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's +environment, because KeyCape resolves `secretRef` at startup. The packet uses a +transient Kubernetes Secret that is deleted in the same session. + +## T02 — Register the probe clients, run the attended probe, then remove them + +```task +id: KEY-WP-0039-T02 +status: wait +depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03] +blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established." +priority: medium +state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3" +``` + +2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md. + +Write the attended script, which prints booleans only. + +2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in +`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused +boolean per case plus the refusal class (`claim_mismatch`, `expired`, +`not_service_principal`), and no token, secret, claim value or OpenBao error text. +A transport failure or an unexpected response is reported as `inconclusive`, never +as a refusal. A token that OpenBao accepts is revoked at once and fails the run. +Still waiting: railiance-platform's re-agreement on the two departures above, and +an attended registry session with net-kingdom. Register the clients +through the normal registration path, hand the run to railiance-platform, record +a value-free receipt in `docs/evidence/`, and deregister the clients afterwards. + +Acceptance: every case is refused, the receipt is committed, and the probe clients +are gone. + + +2026-10-11 source preparation: T01 remains progress because the exact expiry +client and audience-pin amendments require platform-owner re-agreement; the +prior agreement covered the earlier case list, not this revised packet. The +prepared runner revision adds a valid expiry positive control and immediate +revoke, exact decoded claim-isolation preflight (not signature evidence), +semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen +local tests pass; original-source semantic regressions demonstrate two false +successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner +returns, live authority, effective leeway, custody boundaries and cleanup. No +live token, registry or OpenBao action occurred. T02 continues to wait for owner +re-agreement plus separate attended live authorization. MASON-WP-0009-T05 +operator/ESO capability and nonmember checks remain independent acceptance. + +2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans. + + +2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner +acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md) +confirms all 14 executable/test/producer-decision pins and three Platform source +pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing +source acceptance remains valid. Resolved registration go caafe4be covers only +the original secrets-engine-openbao client (KEY-WP-0037-T03, deployed 6f79a2af, +NK-WP-0046 exact-match helper); it does not authorize new probe clients or +coding-client custody, and no renewed original-client approval is requested. +The standing attended rule remains separate, for reviewed commands within +existing scope; no new protocol or blanket approval request is introduced. The +remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof, +admission of the existing coding-client proposal with sanitized owner readiness +returns, then one attended negative/revoke/cleanup receipt committed with probe +clients removed. No native acceptance or credential/registration action is +inferred from this coordinator review. + + +## KeyCape issuer-source compatibility inspection for lifecycle v2 + +```task +id: KEY-WP-0039-T03 +status: done +priority: medium +state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73" +``` + +2026-10-11: Completed KeyCape's own issuer-source inspection for +KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256 +6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed. +Read AGENTS.md, governing INTENT.md, current client-credentials claim source, +registration fragments, native caller-authentication commands and frozen v2 +issuer transport/probe wiring. Seventeen unchanged runner tests pass locally. +This task completes only issuer-source inspection; the resulting narrow +source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment). +T01 stays done and T02 stays open for actual native registration, token input, +refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner +checkout was changed during this source inspection. Owner source files were +read for the bounded issuer compatibility check. + +Current token.go:306-309 returns HTTP 401 invalid_profile_usage with +feature=client_id for a removed confidential client; wrong-secret +feature=Authorization cannot count as removed-registration evidence. The +proposal and packet comments are narrowly corrected against that current +issuer contract. Prior T01/source re-agreement history is preserved; this source +acknowledgment supplies no NetKingdom runtime-owner review or founder live go. + +The private token-producer handoff remains a real execution gap under T02. +Existing keycape login/service-token commands require a new private output +file; login also needs a concrete admitted public PKCE client/callback and +attended browser/MFA. The lifecycle expects an in-memory private stdin object +containing human_token and coding_agent_token, but no reviewed file-to-process +or process-to-process producer, admitted human client/callback, custody-injected +coding-agent source, native placement or producer cleanup receipt has been +provided. No unattended token producer or admitted secret route is invented. +Resolve those exact owner contracts before founder native approval; existing +static registration is not live token custody or producer admission. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed +KeyCape issuer-facing inspection before the new formal acknowledgment in +docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4 +manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8, +executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d. +NetKingdom's independent V4 source review is approved, receipt SHA256 +4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb. +The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain +retained as one correction trial; they are not extra scored successes. + +V4 retains the unchanged issuer runner and six-client claim design, and fixes +finite/nonboolean windows, true total request deadlines, late-known OpenBao +session private revoke/refusal and guarded bounded final Secret deletion. +The issuer-facing acknowledgement covers only stopping late token exchanges, +claim source/shape and preserving token cleanup failure/unknown outcomes; it +does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41 +supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass +locally. No native tokens, credentials, issuer mutations or live cleanup were +performed. T01 stays done; T02 stays open and the genuine private token-producer +handoff remains its next source prerequisite before actual founder native go. + + +2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed +KeyCape's own current issuer-source/claim/cleanup-contract inspection before +any new formal acknowledgment. Current token.go:306-309 still distinguishes +removed-client invalid_profile_usage feature=client_id from wrong-secret +feature=Authorization. The six-client design, audience source and expiry +positive control remain unchanged, and the original runner's 17 tests pass. +Exact V5 manifest SHA256 +d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285; +executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360. +V5 has 46 passing supplied lifecycle checks. It adds a single exceptional +response-close abort under a fixed20ms cap clipped to nonresettable aggregate +cleanup, and to positive original request time remaining, with expired aggregate +zero calls, fixed private errors and truthful known-session revoke/unknown +outcomes. This inspection covers issuer-facing token lifetime and claim +interfaces only; NetKingdom and Platform independently own their runtime and +backend review decisions. Previous V2/V3 Net source refusals and Platform V4 +exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are +historical narrow source inspections, never aggregate native approval. +T01 remains done, T02 open. No native actions or credentials occurred. The real +private token-producer/client/callback/coding-agent custody/cleanup handoff gap +stays the next prerequisite; no unattended admission is inferred. + +Exact independent V5 source decisions were then verified: NetKingdom approved +receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc +(46 supplied +17 runner +21 independent checks); Platform approved receipt +SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0 +(46 supplied +5 independent +17 previously verified runner checks). Those are +foreign owners' own source decisions, not KeyCape runtime/backend reviews or +founder native authority. Formal KeyCape bounded issuer acknowledgment follows +in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json. + + +2026-10-11 T02 continuing source preparation: private producer decision packet +`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI +flows, selected existing netkingdom-bootstrap-console literal-loopback human +client subject to owner purpose/MFA/listener admission, and the existing +codex-railiance-platform source identity subject to actual live/custody/consumer +admission. No existing coding-agent delivery receipt was found; AppRole tokens +do not substitute for issuer JWTs. A bounded pipe-only adapter for the current +CLI's two caller-owned private files has 18 passing synthetic checks, with +identity-guarded explicit file consumption and fixed value-free failure output. +Decoded claim shape does not prove signatures or authenticated admission. +See docs/openbao-negative-token-producer-decision.md and .json for exact owner +choices and missing receipts. The exact trusted workstation-to-node launcher, +private-file allowance and railiance01 Bao route/public-health guard still need +source review and founder disposition before any token/temporary credential/ +mutation/restart. No registration, custody, login, native action or credential +request occurred. V5 and failed review histories retained; T01 stays done and +T02 open. No new work record or native acceptance is inferred. + + +2026-10-11 same T02 source-preparation correction for +KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer +refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink). +V2 has source-pinned private launch/ready handshake compatible with exact +Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before +ANY caller-token file load/consume/write; separate local data/control pipes +reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget +only clips downward after readiness. 31 distinct producer/launcher synthetic +checks and 74 compatible consumer supplied checks pass. No SSH/native/login or +credential access was performed. Stat/unlink remains non-atomic and requires +continuously exclusive sameUID custody; the retained race test is a custody +violation counterexample, not a claim of repaired filesystem atomicity. Receipt +keeps atomic delete/copy cleanup unverified and possible copies true. Exact +trusted launcher/directSSH profile/private tmpfs allowance, selected human +client/MFA purpose reuse and coding-agent registration/custody/delivery remain +unadmitted owner choices. Native node route, role/leeway/window/founder decision +and actual negative/cleanup acceptance remain pending. T01done and T02open; no +new task or plan, and V1/V5-V8/current failure histories are not overwritten. + + +2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair +source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty +now propagates into sticky remote consumer uncertainty before launcher early +return; local SSH termination never proves remote no-effects/recovery. Actual +full-payload then interrupted confirmation and4096-byte partialpipe failures +are retained regressions. 33 distinct source checks pass. Exact Net V9 source +bytes unchanged; new source reviews required, not implicit repin. Native +client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session +authority remain unadmitted. T01done/T02open; no new task/plan or native action. + + +2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal +NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded +nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation +budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not +a renewed launcher allowance. 40 own source tests plus9 retained paired +wrapper/child handshake regressions pass with synthetic/no native transport. +V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact +bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/ +maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/ +credentials/native/owner checkout changes; no task/plan duplication. T01done +and T02open; source correction is not native acceptance. + + +2026-10-11 T02 source-design disposition checkpoint: founder accepted exact +source memo `infd-20261011-key39-source-design-r1`, review +`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`. +Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval +`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject +`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL: +`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`. +Root's authenticated confirmed readback is +`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`, +SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed +`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256 +`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256 +`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction. + +Acceptance covers recommended source preparation for existing bootstrap-console +human-client purpose reuse, returning existing coding-agent custody admission or +preparing a narrow owner proposal, proposed founder directSSH source profile, +and conditional exclusive-owner tmpfs files. It does not establish actual +registration/MFA/listener, coding-agent custody/delivery, transport/file admission, +node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh +attended native go. The published choice documents remain historical exact +pending proposals; this appended receipt records their later source disposition. +T02 remains wait on external owner custody/admission and separate attended +execution authorization. Source acceptance permits further source preparation; +actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03 +remain done. No native or credential action is authorized by this source receipt. +See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact +owner returns and safe metadata evidence needed before preparing a runnable +attended execution proposal. + +2026-10-11 ordered-goal qualification (T02 only): the final source runner already +exists and its 17 local semantic tests pass. No new helper or source protocol is +needed to repeat accepted preparation. Native execution follows the first +Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03); +only those task metadata were checked, not wait-task bodies. NetKingdom owns the +registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns +leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is +already recorded resolved; do not ask again. Attended human authentication and +owner-held existing coding-agent custody go on the single attended checklist. +No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred. + +2026-10-17 Platform owner returns (source-prepared, not admitted): +railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md` +(commit `0a9617a`). The coding client `codex-railiance-platform` has no +admission, custody or delivery record. The existing narrow proposal +`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`) +remains the one to admit, so the coding-agent case stays unrunnable until owners +admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches +the packet's claim design. It declares no `expiration_leeway`, so the expiry +probe must assume OpenBao's 150s default until an attended sanitized read +confirms the live value. The node-local Bao origin is not declared in source and +also needs that attended read. T02 stays wait. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/apply.py b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/apply.py new file mode 100644 index 00000000..c990a50b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/apply.py @@ -0,0 +1,21 @@ +from pathlib import Path +import subprocess,json,hashlib,re,yaml,urllib.request +root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1');out.mkdir() +rel='workplans/KEY-WP-0039-openbao-negative-probe-tokens.md';p=root/rel + +def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode() +base=git('rev-parse','HEAD').strip();assert base=='26ec9bb9b195ad377448570d6b161fd682311a0b';assert not git('status','--porcelain=v1');assert git('ls-remote','origin','refs/heads/main').split()[0]==base +with urllib.request.urlopen('http://127.0.0.1:8000/tasks/03da4c38-1dac-53b1-8b0b-e697df48e9d3',timeout=30) as response:task_before=json.load(response) +assert task_before.get('needs_human') is False +before=p.read_text();old='registration authorization already resolved.';new='original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.';assert before.count(old)==1;after=before.replace(old,new) +blocks=lambda text:[yaml.safe_load(t) for t in re.findall(r'^```task\n(.*?)^```',text,re.M|re.S)] +b=blocks(before);a=blocks(after);assert len(a)==len(b)==3 +for oldblock,newblock in zip(b,a): + if oldblock['id']=='KEY-WP-0039-T02': + expected={**oldblock,'blocking_reason':oldblock['blocking_reason'].replace(old,new)};assert newblock==expected + else:assert oldblock==newblock +assert before.split('---',2)[1]==after.split('---',2)[1] +(out/'KEY39.preimage.md').write_text(before);(out/'KEY39.proposed.md').write_text(after) +baseline={'base_commit':base,'branch':git('branch','--show-current').strip(),'status':git('status','--porcelain=v1'),'index':git('ls-files','--stage'),'path':rel,'before_sha256':hashlib.sha256(before.encode()).hexdigest(),'after_sha256':hashlib.sha256(after.encode()).hexdigest(),'frontmatter':yaml.safe_load(before.split('---',2)[1]),'task_blocks':b,'task_blocks_after':a,'only_task_field_delta':{'task':'KEY-WP-0039-T02','field':'blocking_reason','old':b[1]['blocking_reason'],'new':a[1]['blocking_reason']},'needs_human_before':False,'task_before_public_metadata':{k:task_before.get(k) for k in ('id','status','needs_human','workplan_id','blocking_reason')}};(out/'baseline.json').write_text(json.dumps(baseline,indent=2)+'\n') +p.write_text(after);(out/'one-path.patch').write_text(git('diff','--',rel));assert git('diff','--name-only').splitlines()==[rel] +print(json.dumps({'status':'only authorized blocking_reason suffix changed','needs_human':False,'all_other_task_and_frontmatter_fields_unchanged':True,'base':base})) diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/baseline.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/baseline.json new file mode 100644 index 00000000..403751d4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/baseline.json @@ -0,0 +1,94 @@ +{ + "base_commit": "26ec9bb9b195ad377448570d6b161fd682311a0b", + "branch": "main", + "status": "", + "index": "100644 0e8a5d95b6d263fbaeae4261a7051112fb27ee18 0\t.claude/rules/agents.md\n100644 7c2a64580a2fb55d6586e154ee70d801430faa3e 0\t.claude/rules/architecture.md\n100644 2ab2aee3c221b4c2f637786f66de431236b61bc5 0\t.claude/rules/credential-routing.md\n100644 5b6b1876a29a4de2c3e6fd0f67c3ffbeb144c496 0\t.claude/rules/first-session.md\n100644 b74d916907b49e9abed8a8bd8f4f3cd7aadf95ef 0\t.claude/rules/repo-boundary.md\n100644 c4b8fa16164b5a8b37fee603997121a5c9680aac 0\t.claude/rules/repo-identity.md\n100644 0c2d89e25d9b55e90982f7596f7336328a49c786 0\t.claude/rules/session-protocol.md\n100644 dc53ac69288ec906421a0e8e1e1083528c047702 0\t.claude/rules/stack-and-commands.md\n100644 f43cfff94d75b083b3ca10e7982654d71e75dfdf 0\t.claude/rules/workplan-convention.md\n100644 d3b36c3bffbeb922769a2a788e35b4113fccaa3f 0\t.custodian-brief.md\n100644 b1bcdc2c729352cada7b654b59a07fe6850fc466 0\t.forgejo/workflows/acceptance.yaml\n100644 0e16cc2ea75d37e1da788ca847c127fc04f5a98f 0\t.forgejo/workflows/image.yaml\n100644 e8491b77aad0b288977d3fb5eb8ca8db4a7725de 0\t.gitea/workflows/image.yaml\n100644 32a386e9115c36d1689d9ff595eab8d66f037286 0\t.github/workflows/ci.yml\n100644 e705f7f1738a49dd9a78b5907d68d9429107c67c 0\t.gitignore\n100644 4fc521fc9c4263b37c6978ea68651d1cb0c25faf 0\t.repo-classification.yaml\n100644 4a0df33e6635969b7158daec3e80bd28e5f0bbc1 0\tAGENTS.md\n100644 e81c1b48c16dc3bf49af30e48b476107d916ee21 0\tCLAUDE.md\n100644 6adf9ce0636d7782fd231e418a0009deeee9868b 0\tDockerfile\n100644 b2519df87f5b29b324187f97f7a0d92dff3fc874 0\tINTENT.md\n100644 7e08e28d5f32bdea3a9e1c828ae9f1e21d9edae7 0\tLICENSE\n100644 512fa11a68e460d410deff823e58b9608a342932 0\tMakefile\n100644 5435b16714d66247db4d99ab7782cf8cd21532d9 0\tREADME.md\n100644 ab2522b2e0c0e6c008810e9929fc0b8d35121512 0\tSCOPE.md\n100644 d86b29bbc480acfa6d12d5052039578d7d4e380c 0\tWORK-RECORDS.md\n100755 e7887dfbae490351f88d1f249e928f695f6a16d2 0\tbin/keycape\n100755 592504fd11f4c4fc821f3d6b0350b1285c38b0b0 0\tbin/keycape-to-keycloak\n100755 e49fe216aa04525d611c99ddf930639f98929ad0 0\tbin/lldap-export\n100755 9609697bd8e21396add3009893fcd8a11352eabd 0\tbin/lldap-to-ldap\n100755 b732e3274c05371e62768da3c3bdc3ac767329a0 0\tbin/validator\n100644 b43901fe8a7c3ca61f509b5cb2eac8b2ef091064 0\tcapabilities/playbooks/key-cape.lightweight-sso.yaml\n100644 2872ef7727ac0fc2c3943296098fbea6f2a85f62 0\tcapabilities/playbooks/key-cape.privacyidea-token-authority.yaml\n100644 bf8ea915b24d93be803ed1ef8c18c13a82976c9b 0\tconfig/dev-config.yaml\n100644 a4b13e5cffdc1cb683a202b90724bd86f565ccda 0\tconfig/service-clients.example.yaml\n100644 fa050d2a3722fe7372938ec2cb34b31244b7aa61 0\tdocker-compose.dev.yml\n100644 a8976c28eb6c40af92e1daadcf168aaef8647ad5 0\tdocker-compose.scenario-b.yml\n100644 5743fa39140e5bac1f2cad2479c5928470e76e1c 0\tdocker-compose.scenario-c.yml\n100644 52ab3e94c61c02ea0b13a0b8cf4e3f7d0f89eb40 0\tdocs/adr/ADR-0001-choose-go-for-keycape.md\n100644 f42bfba2462bbc739f409bb817ab85904236e3d1 0\tdocs/approval-clients-deployment.patch.yaml\n100644 08b3e12ea62dd0da1354c59060434b88c9e7040f 0\tdocs/approval-clients-rollout.md\n100644 3c35fe6242d82b72a0a66b3da17f8e5dd078df2e 0\tdocs/approval-engine-auth-contract.md\n100644 902c20cb3cfa6d4592947ac76df88a2fa5186649 0\tdocs/approval-engine-provisioning-request.yaml\n100644 f305058adf60bef56762c2588b5b5b2474f45430 0\tdocs/authorization-code-bindings.md\n100644 de742b60f5872ad84195f33f2a5763952653fcd5 0\tdocs/credential-renewal-release-2026-09-13.md\n100644 c2061c6a760e810adccdc404e044cb79baa4fdcb 0\tdocs/enrollment-assurance-release-2026-09-13.md\n100644 85dab93ed1f2196810ead4f6b81a897fd06c33ff 0\tdocs/evidence/2026-09-08-upstream-issuer-live-proof.json\n100644 6ae6fc721df48426e93e505206a0df8e18ef2499 0\tdocs/evidence/2026-09-09-keycape-verifier-admission.json\n100644 62fe341c90c514a92585641d14287f60076e8836 0\tdocs/evidence/2026-09-09-upstream-issuer-pin.json\n100644 cc87339baeae5406bd7ce76fe89bd0aaf95b366f 0\tdocs/evidence/2026-09-14-informed-decision-browser-registration.json\n100644 200863c7e6b87f3045c1bffd2b951be1bdee5d84 0\tdocs/evidence/2026-09-24-fresh-login-and-account-switch.md\n100644 2278d9917b56923887b64b157b10de537739ee55 0\tdocs/evidence/2026-10-07-secrets-engine-openbao-issuance.json\n100644 d97906b5ee836fca2e52bbe42a462b02200f4fe5 0\tdocs/evidence/2026-10-11-key39-next-source-inspection.json\n100644 88d98b835b63a016ede535cdf6416add36c17514 0\tdocs/evidence/upstream-issuer-probe.json\n100644 04d0bccc32fa1e0d5739f22b5d71a638e345e386 0\tdocs/forgejo-oidc-client-request.yaml\n100644 e714df21c4113ac853f2fbbf9309c06ea1b05863 0\tdocs/native-authentication.md\n100644 9f3c00bf7dcef2b3fa40a1c0fd99a7c03cb8facf 0\tdocs/offline-checker-owner-review-2026-10-16.json\n100644 be834c5854e2d1ac3f2747fb45dd80953508ff45 0\tdocs/offline-checker-owner-review-2026-10-16.md\n100644 259f7e10fb0a6aaa071aac342906cde85c056609 0\tdocs/oidc-browser-relying-party-type-review.md\n100644 713036618418c02f612071f41c4864b9b67c900a 0\tdocs/openbao-negative-probe-clients.yaml\n100644 fe9a9945d837fec98dc15452c1e7935b6a4fd0f1 0\tdocs/openbao-negative-probe-session-proposal.md\n100644 d3b806af1dd024bfc0f518d30ef26e1b715be361 0\tdocs/openbao-negative-token-producer-decision.json\n100644 e44cbcb98a7bb4efd727a026e70db45f5195fa30 0\tdocs/openbao-negative-token-producer-decision.md\n100644 3416a5943e1b535870d16841c10a89fdb65521c8 0\tdocs/openbao-service-auth-contract.md\n100644 3092a16568b37610407e4d613b01a18bead5e960 0\tdocs/operations.md\n100644 f95f57aaa5cfd6203ea5d1b0ce99369ec32c40cc 0\tdocs/optional-mfa.md\n100644 2be7eb82c4ee1ec9a4b9227d2163b063063e40f7 0\tdocs/proposals/2026-10-11-key39-founder-source-design-decision.json\n100644 ad23cbde3d063f40e85948d0b37ec99847f24840 0\tdocs/proposals/2026-10-11-key39-founder-source-design-decision.md\n100644 09fe2e81cdda1372fc0ed7ec2e3103d303d7211d 0\tdocs/proposals/2026-10-11-key39-next-execution-prerequisites.json\n100644 714a84c4a5d9e9258110b703297763537bbd07ec 0\tdocs/qonto-runtime-identity-contract.md\n100644 315a7210651a2777eeca2a5f502cfa5b91207600 0\tdocs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json\n100644 6c462037bb51a833451f37ddcca6a347656f354d 0\tdocs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.sources.json\n100644 af94cc71214d638d2e33cdb455b29c9ef5cfb1f4 0\tdocs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json\n100644 1a1ebdddca7e3e1c419ef293466174644dfdec1b 0\tdocs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.sources.json\n100644 9ad2ee43cf5c66a66ff0472f4c9f40d0e550283a 0\tdocs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json\n100644 567ee0f3aa1b14f8633e874a35530eb76b8c93c4 0\tdocs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.sources.json\n100644 f0aa85ec5484751c8e490e2c60a77a600e21a1e1 0\tdocs/reviews/2026-10-11-key39-v7-input-keycape-source-review.json\n100644 f4d4d9013656d1039585e32e3a9777eab096fa71 0\tdocs/reviews/2026-10-11-key39-v7-input-keycape-source-review.sources.json\n100644 2b5ccdb5341c43030ff87a62d418e2322d4e75af 0\tdocs/reviews/2026-10-11-key39-v7-input-keycape-work-record.json\n100644 7e4d3ec75f7671b2327f7eccfc51d1d902db275f 0\tdocs/reviews/2026-10-11-key39-v8-input-keycape-source-ack.json\n100644 1d9a58cd94ceba2d4727060eaf13cc0442240352 0\tdocs/reviews/2026-10-11-key39-v8-input-keycape-source-ack.sources.json\n100644 69a2ab4de8cbe2987a2be322648aa90ace9e26d6 0\tdocs/reviews/2026-10-11-key39-v8-input-keycape-work-record.json\n100644 553e90a108f111a9c42b88e16e1f38ff38af3231 0\tdocs/reviews/2026-10-11-key39-v9-v4-keycape-source-ack.json\n100644 524f47af20bae995988f59285c5f29283261e007 0\tdocs/reviews/2026-10-11-key39-v9-v4-keycape-source-ack.sources.json\n100644 acf3e488fa9af74216f669e3f84bdf5d22e3963b 0\tdocs/reviews/2026-10-11-key39-v9-v4-keycape-work-record.json\n100644 2b7e30ef9b82ebbf22ad589d31d0fccb74b33c95 0\tdocs/secrets-engine-openbao-owner-packet.md\n100644 ce5f53365f2ae3b14cd53f4b43c248f77c5a0223 0\tdocs/sitting-requester-registration.json\n100644 d45478c9ef6363a735fd3605417e9275c18a2a94 0\tdocs/t03-requester-registration.json\n100644 32efcf0a5afc5c660bd754eb494020c32f609bdd 0\tdocs/t03-requester-registration.md\n100644 9ab277b85b550ac52ccf98bfda0a0dd7c744e171 0\tdocs/tenant-claim-contract.md\n100644 4e7d26b6a1cbcacf6498aed0e33da8964160c39e 0\tdocs/upstream-issuer-proof.md\n100644 4f305e21acfa7ad7f44b70542ba8f627b5ef9362 0\thistory/2026-09-05-011726-scope-intent-assessment.md\n100644 394eed11f5afb16de7eda7b09f99cf59dbbbacce 0\thistory/2026-09-09-scope-reassessment.md\n100644 2c9e75f72d9983b1b2980d79b12380564465e640 0\thistory/2026-09-28-railiance-admin-role.md\n100644 77233c20c2230459b2d78d9d28578e9472e568ce 0\thistory/KEY-WP-0011-live-secret-exposure-recovery.md\n100644 8eef81bac925dacfa8408bdfb4e5a2d672ba903e 0\thistory/KEY-WP-0011-rotated-credentials-2026-08-23-final.tar.age\n100644 845c3025f781d2296f40b4ec93c43935a480295d 0\tintakes/intakes.md\n100644 569abe99c95e163d39048f4b02ccb47165751c7e 0\tregistry/README.md\n100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0\tregistry/capabilities/.gitkeep\n100644 59cc40e2ccad5b2ce9111366efbc9e48f8e18e70 0\tregistry/capabilities/capability.iam.key-cape.md\n100644 0718f5218c9f4b4ef84c6d9aacd4b61440098405 0\tregistry/indexes/capabilities.yaml\n100755 bf05bb3db90d62b77a1104e48d8e31045ccf0d8e 0\tscripts/bootstrap-dev.sh\n100644 29627c2056fbcf511bce069db0a8803c797295ff 0\tscripts/collect-upstream-issuer-proof.py\n100644 10832318492c4b34230400e458209777d58b08a1 0\tscripts/factor_recovery.py\n100644 2bc28a205ffbd14c4f077c61957f5e3c0af284fb 0\tscripts/keycape_onboarding_guard.py\n100644 e3a875e114d176431b517667c0c096ac4a974ad2 0\tscripts/openbao_negative_private_handoff.py\n100644 4eae60ab0d0766a390743ed33d665ea852bb285e 0\tscripts/openbao_negative_private_launcher.py\n100644 cb864ae0b2b46650a5e20ae90e9465701c362011 0\tscripts/openbao_negative_probe.py\n100644 477857655b8316659d7a3a2afcbe330e497d3851 0\tscripts/provider-browser-fixture.py\n100644 5ac86bfec073a054b712fa7a82fd44066f203a29 0\tscripts/provider-onboarding-contract.py\n100644 2abdf8d67ab9a61f93ab3ef90d36942ee7659702 0\tscripts/provider_browser.mjs\n100644 bc9f59737a06c59e7746aecd00df8112a9c830d1 0\tscripts/provider_browser_acceptance.py\n100644 ca0bb2a3554c569d3923c48bebe0850a1f4254ff 0\tscripts/recovery_service.py\n100644 0f2dca8e890f18bf5430596a1c722bdc4bcb4d0c 0\tscripts/render-upstream-issuer-probe.py\n100755 48a5832ed965624685eeeb9cad396b8e6a854426 0\tscripts/test-scenario-b.sh\n100755 e333ec26464ade014e45d7a13b153d0a6f29b0a1 0\tscripts/test-scenario-c.sh\n100644 25d961f46258329f2290811101388c293e16b025 0\tscripts/test_factor_recovery.py\n100644 c17061849c2a8f9b6c614e69888502607c612674 0\tscripts/test_openbao_negative_private_handoff.py\n100644 e1d98a393d5276d64d689c3b961790219e939f77 0\tscripts/test_openbao_negative_private_launcher.py\n100644 8344b22b4d62deb60ccafafa7acca336d226b0a8 0\tscripts/test_openbao_negative_private_preparation.py\n100644 242d4502ab1f2bda9886ec0936c647019c426696 0\tscripts/test_openbao_negative_probe.py\n100644 6cdb5acd55440ed089e2842f5c762627363defa1 0\tscripts/test_recovery_service.py\n100644 ab6f45297f354d006c4e24beb698bafe8a93a31b 0\tscripts/test_render_upstream_issuer_probe.py\n100644 fca9b9d450cdee91d383ff8a67c738774c5aca4c 0\tspec/canonical-model.yaml\n100644 41cc35711da626fd1986f0da439b230235cac4df 0\tspec/ldap-schema.yaml\n100644 083df0b3e537a5ebb09b9a454f25c68d650fdac8 0\tsrc/Makefile\n100644 faca3748339a924aff723dcb3ed10ec8d737248f 0\tsrc/cmd/keycape-to-keycloak/main.go\n100644 5edcfd7d4fe8f6af021676e8f0f8a0b258831c37 0\tsrc/cmd/keycape/checkconfig.go\n100644 93e4c44654ca28fb16027748c9c24d91d5ced4bd 0\tsrc/cmd/keycape/checkconfig_test.go\n100644 c2b60259d95831503e35a0c67fbe6e3d0c4169f3 0\tsrc/cmd/keycape/clients_test.go\n100644 16de34c66062718b91821d0b836e258c82783401 0\tsrc/cmd/keycape/main.go\n100644 daac6e986c48fcf2de9243ef78a40e0b88b9de4a 0\tsrc/cmd/lldap-export/main.go\n100644 5aa9a2bb1472931b7b9c798fbebfee45fe316f9b 0\tsrc/cmd/lldap-export/main_test.go\n100644 c98b19b31f2c2be0124c88f001b33048a7221968 0\tsrc/cmd/lldap-to-ldap/main.go\n100644 411272078c48cc52abce1007c0939528c66581fa 0\tsrc/cmd/validator/main.go\n100644 992c33f91d6b7f1244634cf583072f4bfbf73759 0\tsrc/go.mod\n100644 d1f81c7aef0bb7c7892e9dd2131653661659bd0b 0\tsrc/go.sum\n100644 b78811acf78adad92d9a412c13cbb7aa86866468 0\tsrc/internal/adapters/authelia/adapter.go\n100644 81ac6205d8217509e6130b159c77e94291ca55a5 0\tsrc/internal/adapters/authelia/adapter_test.go\n100644 810f5b9387721058fb629a305d3aaf5254c65a12 0\tsrc/internal/adapters/authelia/config.go\n100644 082708a178b818d83632bfe31c77696c03530c60 0\tsrc/internal/adapters/authelia/issuer_probe.go\n100644 7943c3f59d5c59aba5adaff9dab1af4d4055d635 0\tsrc/internal/adapters/authelia/issuer_probe_test.go\n100644 7c85d3b9ea7d7843092b24d812807fb6b48755dd 0\tsrc/internal/adapters/authelia/verify.go\n100644 d9003680dc01f3d709d938ff8843f0a96b8a69f6 0\tsrc/internal/adapters/authelia/verify_test.go\n100644 e15cecd7c7ee83383745a367502bf6ab3d1280fe 0\tsrc/internal/adapters/lldap/adapter.go\n100644 e153314a0fc06e33ce8b957b66cc1838ee93a180 0\tsrc/internal/adapters/lldap/adapter_test.go\n100644 fce760a4e97c94e1e542a4d2f0895a20c709fed9 0\tsrc/internal/adapters/lldap/config.go\n100644 d9e10ff898c19e85160e8b047f79a1d9daa797c7 0\tsrc/internal/adapters/lldap/config_internal_test.go\n100644 d76e938ebfc2f676d5dd0a0639707d1e7f4ebc08 0\tsrc/internal/adapters/lldap/railiance_admin_test.go\n100644 35e65335a00ead963c3cd8e95e1b94a5b34e6a5a 0\tsrc/internal/adapters/privacyidea/adapter.go\n100644 286562d56a3f263df68f34eb35bf68b6581688da 0\tsrc/internal/adapters/privacyidea/adapter_test.go\n100644 b34c9f3d19109a7a0acc309c27639ce913f1e751 0\tsrc/internal/adapters/privacyidea/config.go\n100644 017315272e6baa1151d4e73912b19c3bb8ca377b 0\tsrc/internal/adapters/privacyidea/credential_file_test.go\n100644 6f3b883c0c7eced42bfdbe45713b1be5dadf0bc9 0\tsrc/internal/adapters/privacyidea/enrollment_state_test.go\n100644 13316d5ffada681a8f956b519f5a3d6ef94c3233 0\tsrc/internal/adapters/privacyidea/optional_mfa_test.go\n100644 964fbb76048c44616d56b7da916d44c6b86432f0 0\tsrc/internal/adapters/privacyidea/read_scope_test.go\n100644 eeb54b9f0b7e1b9776fc37ecfc36599bbf07026a 0\tsrc/internal/adapters/tenantengine/adapter.go\n100644 d437fd14dcfffa53999b16849888a86c553d0a42 0\tsrc/internal/adapters/tenantengine/adapter_test.go\n100644 2b0417e3745a31e177d2b154454a0f132d2f7616 0\tsrc/internal/authclient/cli.go\n100644 0d39586235d89aa8097d76b7d210331e4a53083f 0\tsrc/internal/authclient/client.go\n100644 dfce39882584ab7f37ecb9878d6b1801b74d9a4c 0\tsrc/internal/authclient/client_test.go\n100644 66b0ea4961b3573152c491f597fad3e02ea59a1a 0\tsrc/internal/authclient/verify.go\n100644 a2a824f02554f0645a543934ee33419dca429085 0\tsrc/internal/authclient/verify_test.go\n100644 f28ad987f862203708795498947229df9de3757e 0\tsrc/internal/config/account_test.go\n100644 7895b73a96899a5443a0a044108e3dd3e4e76294 0\tsrc/internal/config/config.go\n100644 d4236a6d45fa57b865b0983b395c6e9b3974105b 0\tsrc/internal/config/config_test.go\n100644 585aa6243370bb60a94506f5fa670516959c12b0 0\tsrc/internal/config/optional_mfa_test.go\n100644 c020e4076637f2f062ae49a8578bea8dd493bd70 0\tsrc/internal/config/validate.go\n100644 afbc48c673df6fe76ed3f2fa2aea492e476ec37b 0\tsrc/internal/domain/assurance.go\n100644 b9096ef7c09da6a413ed392c751799b3bb1b72bf 0\tsrc/internal/domain/assurance_test.go\n100644 66c907c367651b193559701c2da564aea6757ae8 0\tsrc/internal/domain/auth.go\n100644 5984835dd46da65b095829e05948f841554f733f 0\tsrc/internal/domain/conformance_test.go\n100644 30b0a92d9444324cdf67df1e856feb984385c29e 0\tsrc/internal/domain/mfa.go\n100644 1cfda5371fe0c6eec07329b4990d3c6a105e92ee 0\tsrc/internal/domain/model.go\n100644 56252d821062128e3323a0cdca1064d292a97898 0\tsrc/internal/domain/repository.go\n100644 80ebf0763538369dff67abd3a17c6269022f5e2e 0\tsrc/internal/errors/taxonomy.go\n100644 2f5722e94e982c71b008b7f4c522d500df76a0e1 0\tsrc/internal/errors/taxonomy_test.go\n100644 14769bbe206e8fbbf6eb0ed4212e3ad656ffd996 0\tsrc/internal/jose/jose.go\n100644 34abfa36f8dd355df7713eb940b269f785805402 0\tsrc/internal/jose/jose_test.go\n100644 4119e8a8950a8461c5fe5cb4944af498df5df84f 0\tsrc/internal/migration/lldapexport/exporter.go\n100644 e9071d133a6f3fbd2421d3d0dd9fc8a051d61693 0\tsrc/internal/migration/lldapexport/exporter_test.go\n100644 4666a2b8579e73f76bd4ccf4518cdd9818035e2a 0\tsrc/internal/migration/tokeycloak/transformer.go\n100644 50dee2c66b1922e2d109ca963592fe3a0bf9711c 0\tsrc/internal/migration/tokeycloak/transformer_test.go\n100644 fd21830fd2299a401000bb418946d107bbefa1ee 0\tsrc/internal/migration/toldap/generator.go\n100644 5ec399dca0db878f3d5fab76504947635770727d 0\tsrc/internal/migration/toldap/generator_test.go\n100644 81fd2c8d24016adb626f7a88b442361cf6197124 0\tsrc/internal/server/errors/enforcement.go\n100644 0f8384bc5a147905ea99f33d1a4e573c7a85387e 0\tsrc/internal/server/errors/enforcement_test.go\n100644 7036039edd2837cb22e804b60633b2425a5fb817 0\tsrc/internal/server/oidc/account.go\n100644 1a2d68f1008418e138a6c2d293f2afda017c5ba5 0\tsrc/internal/server/oidc/account_test.go\n100644 72ad579561c84e9ed99da98dd86ca472880ff123 0\tsrc/internal/server/oidc/assurance_time_test.go\n100644 fb5b1f41c0619b7ea6d72c231aec2027769f7f77 0\tsrc/internal/server/oidc/audience_test.go\n100644 60447bc9cd056c2119b1611e38585bc81884a568 0\tsrc/internal/server/oidc/authorize.go\n100644 434b51089fe92497644cdc4504f765f75c21f542 0\tsrc/internal/server/oidc/authorize_test.go\n100644 4b0074b6b5376f11c3979b13b05761c521762bac 0\tsrc/internal/server/oidc/discovery.go\n100644 f2d069d54f67ac1cad061e53687a44ab0c1648ba 0\tsrc/internal/server/oidc/discovery_test.go\n100644 31954ca4dd03ccc36d9da03d5734ac029d46c9ce 0\tsrc/internal/server/oidc/handoff.go\n100644 d8a33fb4e5f90f92e7acab2f198c540d39cfdebc 0\tsrc/internal/server/oidc/handoff_test.go\n100644 d73a3ffcd40558731fb4fac2c66ff0588c0969f5 0\tsrc/internal/server/oidc/hardening_test.go\n100644 187bd993cb85369ada263c734bd4e7daec5bbb73 0\tsrc/internal/server/oidc/human_tenant_test.go\n100644 e74bd2e4a219c25aefc781841e73eb219e0945a5 0\tsrc/internal/server/oidc/jwks.go\n100644 1fd53f19e6c8a131a639d780bea7e1c0c609930a 0\tsrc/internal/server/oidc/jwks_test.go\n100644 7953f06b4948162868166a8ffafef36b2445509b 0\tsrc/internal/server/oidc/login_session.go\n100644 496d237a3f642ac94c869f0dc3438d9a2e262b9d 0\tsrc/internal/server/oidc/logout.go\n100644 47d4df2dee47489a0923df57fe654be87edf2b12 0\tsrc/internal/server/oidc/native_optional_test.go\n100644 2f8eecda6f29cf3d31177ec3b7ec3650a871b555 0\tsrc/internal/server/oidc/optional_mfa_test.go\n100644 221ed02325c872a7947e798607da236f15d7d6e3 0\tsrc/internal/server/oidc/policy_isolation_test.go\n100644 c70d6a4a7d78abd6c0911776f8857ddedb7858bd 0\tsrc/internal/server/oidc/policy_runtime_test.go\n100644 d1c9a9660f3e5fe6c878657957ce6bd7db175c6d 0\tsrc/internal/server/oidc/principal_type_guard_test.go\n100644 15d759c148e72d4a84df18a3d62db1c6838d1be5 0\tsrc/internal/server/oidc/session.go\n100644 ba82ee4b244e241d8d156e05315d8b71626b1cef 0\tsrc/internal/server/oidc/signin_diagnosis.go\n100644 72a1b3518b8824da04d1bf87bd4d73dfa1a7a934 0\tsrc/internal/server/oidc/signin_diagnosis_test.go\n100644 a955146312249d526fa7fedefd085c1140399ac0 0\tsrc/internal/server/oidc/tenant_precondition_test.go\n100644 1517610493b1e91c16f9bfee5fd63361be2d7992 0\tsrc/internal/server/oidc/tenant_test.go\n100644 b1d7f57106208d5cc8a80d964e1698d1dc59dd91 0\tsrc/internal/server/oidc/token.go\n100644 42314ec0fe6c254c3b29c4f859e090f18b316f21 0\tsrc/internal/server/oidc/token_test.go\n100644 c3d2014bd63dc2186d8a2123cef064b2cddfef36 0\tsrc/internal/server/oidc/userinfo.go\n100644 5a12f5e898c8f4526d505404eed14e6f466ca7d7 0\tsrc/internal/server/oidc/userinfo_test.go\n100644 29167da1dbb2b3a9d2e74f5f0319e0902d4173a3 0\tsrc/internal/server/policy/policy.go\n100644 4becee709136b31735eeeab3f1b6b1875de4407e 0\tsrc/internal/server/policy/policy_test.go\n100644 e0cfefbf7c014370890470fc42850c3b3a879daa 0\tsrc/internal/server/readiness/readiness.go\n100644 02cf96ed77554ce25eb4f62e54c00b02cc0089a5 0\tsrc/internal/server/readiness/readiness_test.go\n100644 ff11c05a76db4692e1de6d9ba19f5dc5d45ed2e8 0\tsrc/internal/server/telemetry/emitter.go\n100644 3d64e3d148536f96bddecd228c7eba6711194780 0\tsrc/internal/server/telemetry/emitter_test.go\n100644 793b79bcef80638f2c182645cba9773c17c1550c 0\tsrc/internal/server/telemetry/events.go\n100644 87baa854418f0a6617579e6566aa84217134c6e2 0\tsrc/internal/validator/report.go\n100644 78eb866cef192174b0a29f151eac64f4adc85472 0\tsrc/internal/validator/validator.go\n100644 a86eff31eb55d9cda811adc3508b44e82f6b2eba 0\tsrc/internal/validator/validator_test.go\n100644 75cddf3602a0dbb1d29162db1df187e43e3d4568 0\tsrc/tests/conformance/external_test.go\n100644 208e903a6377e3b894595de8c97248ba96e3dbb3 0\tsrc/tests/migration/fixtures_test.go\n100644 8a49ddec84f156a2ef0badf8f78d292243b7a5dd 0\tsrc/tests/migration/scenario_b_test.go\n100644 bf0a61a1c7e00fd6f3e919529603c5c7461f4a69 0\tsrc/tests/migration/scenario_c_test.go\n100644 377b0f33da592cfbc7eeb9bc74025280ce553a6d 0\tsrc/tests/negative/negative_test.go\n100644 8b802f71346bf27a58ae73877fa594be0557b396 0\tsrc/tests/profile/profile_test.go\n100644 9192fd9cde67896f741cf67dccd48d2250d6ad27 0\ttools/register-informed-decision.py\n100755 0dcb542eeb16827ccb0d0f05b6e302f6af1693e8 0\ttools/rotate-live-exposed-secret.sh\n100644 4b728fd406c815143f302a76512858d3577e6aca 0\twiki/KeyCapeSpecificationPack_v0.1.md\n100644 c803cabd2342689c3123e71b8773edb8a972125d 0\twiki/KeyCapeSpecification_v0.1.md\n100644 f7e78675df62c9b48e29f882fa99298d6d8ce7f1 0\tworkplans/ADHOC-2026-09-05.md\n100644 17401073c053c9710e6e680243bbd313b08d1fc1 0\tworkplans/ADHOC-2026-09-07.md\n100644 0b755547aa7d5589f0898df2f102a45b6aa24838 0\tworkplans/ADHOC-2026-09-27.md\n100644 74334411cd056e7c7253e84f1c4b40c42eb13de3 0\tworkplans/KEY-WP-0001-keycape-implementation.md\n100644 418f477c0ede13d7c003d744abf234347f5d5e31 0\tworkplans/KEY-WP-0002-container-image-gitea.md\n100644 9896c67d78f0efdd03325535eb41295250be3a19 0\tworkplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md\n100644 a50e910558ee43073c92cb27865a1aceac990dd5 0\tworkplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md\n100644 648d2405699dcf25b3121df6c46d871597100d7b 0\tworkplans/KEY-WP-0005-iam-profile-core-claims.md\n100644 632c4c2cad39cbde55c2ae1f2ba5ecf2f445b1a0 0\tworkplans/KEY-WP-0006-client-credentials-service-tokens.md\n100644 f1b04e1a61b398426c23d8d9ca655b3937d7bd10 0\tworkplans/KEY-WP-0007-user-engine-portal-oidc-client.md\n100644 438ab5480de3bd66d75b6ec7e09639d1b93e3e5f 0\tworkplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md\n100644 c9e585db65424c7c4e3b5adf37a68de2bdfbdc1c 0\tworkplans/KEY-WP-0009-provider-capabilities-and-service-identities.md\n100644 81614eeb8c98205d6e5eb3201da10e36b76a21e9 0\tworkplans/KEY-WP-0010-openbao-operator-loopback-callback.md\n100644 f7b906f552f6e3279895a90afbe216b6e78ee5ff 0\tworkplans/KEY-WP-0011-live-secret-exposure-recovery.md\n100644 0f3deb626e5de9a9f898754ef81d3f2ee5282792 0\tworkplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md\n100644 7d77aee3caf3d79fe6d6c1369249c6d94e0ae123 0\tworkplans/KEY-WP-0013-approval-engine-resource-audience.md\n100644 bacf04c07f59125a90c519d3c306a7414cb9a1a3 0\tworkplans/KEY-WP-0014-native-credential-lane-handoff.md\n100644 82b952a216346ffb7f5cab413623fe4a88f7700e 0\tworkplans/KEY-WP-0015-scope-intent-assessment.md\n100644 52839bd0f83d97656c8ad97a8ffc338d9019239a 0\tworkplans/KEY-WP-0016-authorization-code-protocol-hardening.md\n100644 00eb03971aae75724afbf7b8a4946ce6b7eda3af 0\tworkplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md\n100644 325fc483d62dbad53b92b986de56112168a92b4a 0\tworkplans/KEY-WP-0018-export-completeness-evidence.md\n100644 dfeb92296f46d4ce2ac7df1fedcc61e9ccc5d6b3 0\tworkplans/KEY-WP-0019-upstream-provider-token-verification.md\n100644 7b6f3be976293469dee2180c10e16f313876d640 0\tworkplans/KEY-WP-0020-migration-contract-preservation.md\n100644 1f6efeb32a6eb8fcbc52f4d3b591e2fc6b9e006d 0\tworkplans/KEY-WP-0021-snapshot-attribute-validation.md\n100644 7c69629ecd20d0158aa1332d9d1442b796f6a7af 0\tworkplans/KEY-WP-0022-replacement-harness-and-external-conformance.md\n100644 3e7c83cb12e7fc31e682cf3c27f4c4767ded2905 0\tworkplans/KEY-WP-0023-live-migration-proof.md\n100644 baa0bbd195a92708f2befd027b938fcd1812ced7 0\tworkplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md\n100644 a53e646761178748e8c4b00dcde888698447a948 0\tworkplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md\n100644 1f96a2ac3ac939c4e6e0f66172477cf73db04e51 0\tworkplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md\n100644 57e01b2db033847cc786504b8795e37112d988a7 0\tworkplans/KEY-WP-0027-rollout-readiness-and-live-state.md\n100644 46b0672fea90cd7cb672f7d119706c240474f8ca 0\tworkplans/KEY-WP-0028-browser-client-field-validation.md\n100644 392f97750f6cb8b12868a6fd016c7d396b58477e 0\tworkplans/KEY-WP-0029-scope-reassessment.md\n100644 0f74e0d5a037eec64b11b57c67b02057a3b0bfef 0\tworkplans/KEY-WP-0030-tenant-precondition-guard.md\n100644 d2ede9d494927976ccb9ba6a5aeaf1ed21c30837 0\tworkplans/KEY-WP-0031-gate-house-tenant-ruling.md\n100644 a04f52f817a51efc1d0d0ecb99efacea8dc53a17 0\tworkplans/KEY-WP-0032-principal-type-provenance-guard.md\n100644 5b478f953890339aec796c11a536af0ec3238789 0\tworkplans/KEY-WP-0033-vergabe-fresh-login.md\n100644 eb58f99bc26e3c3ac33230dfe432c8b1fabe47ce 0\tworkplans/KEY-WP-0034-account-recovery.md\n100644 29eb711f656c91808e91f3981a9168c3a0ebc253 0\tworkplans/KEY-WP-0035-optional-mfa.md\n100644 fd296bc05a6e565b0ef6e88a9ae3daa7c9f81786 0\tworkplans/KEY-WP-0036-platform-factor-recovery.md\n100644 ad7fed77a27aa8487693ad6fcf342502f418427a 0\tworkplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md\n100644 8cd5bc66f7b0115eec7813d6eec994a1bb618668 0\tworkplans/KEY-WP-0038-forgejo-oidc-client.md\n100644 b22aa6a1116412ab60586163a4fd759270f1b6bf 0\tworkplans/KEY-WP-0039-openbao-negative-probe-tokens.md\n100644 8e8730c8e7733b13d6a9f42689a9d8e407769e81 0\tworkplans/KEY-WP-0040-mfa-attended-run-findings.md\n100644 56fd05819e3acd37b8e6cb468ec3d62307f1ae7a 0\tworkplans/KEY-WP-0041-sign-in-diagnosis-page.md\n", + "path": "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md", + "before_sha256": "618600946f6dd4128206db7ab23da9cc96e22cb84e0fd301b1b2eacd5c9bdba4", + "after_sha256": "517f9526d574ce3faafad18837e0039db8f3f7990eac3e34a1678cdd7307a9e1", + "frontmatter": { + "id": "KEY-WP-0039", + "type": "workplan", + "title": "Negative-check tokens for the secrets-engine OpenBao JWT role", + "domain": "infotech", + "repo": "key-cape", + "status": "active", + "flavor": "implementation", + "owner": "claude-code", + "topic_slug": "netkingdom", + "created": "2026-10-10", + "updated": "2026-10-17", + "depends_on": [], + "related_repos": [ + "railiance-platform", + "secrets-engine" + ], + "state_hub_workstream_id": "09c3bddf-6058-5603-8488-cccdb7be4f86" + }, + "task_blocks": [ + { + "id": "KEY-WP-0039-T01", + "status": "done", + "priority": "medium", + "state_hub_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8" + }, + { + "id": "KEY-WP-0039-T02", + "status": "wait", + "depends_on": [ + "KEY-WP-0039-T01", + "KEY-WP-0038-T02", + "KEY-WP-0038-T03" + ], + "blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.", + "priority": "medium", + "state_hub_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3" + }, + { + "id": "KEY-WP-0039-T03", + "status": "done", + "priority": "medium", + "state_hub_task_id": "54d37ad8-2352-51c2-bb2e-af6070dbbd73" + } + ], + "task_blocks_after": [ + { + "id": "KEY-WP-0039-T01", + "status": "done", + "priority": "medium", + "state_hub_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8" + }, + { + "id": "KEY-WP-0039-T02", + "status": "wait", + "depends_on": [ + "KEY-WP-0039-T01", + "KEY-WP-0038-T02", + "KEY-WP-0038-T03" + ], + "blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.", + "priority": "medium", + "state_hub_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3" + }, + { + "id": "KEY-WP-0039-T03", + "status": "done", + "priority": "medium", + "state_hub_task_id": "54d37ad8-2352-51c2-bb2e-af6070dbbd73" + } + ], + "only_task_field_delta": { + "task": "KEY-WP-0039-T02", + "field": "blocking_reason", + "old": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.", + "new": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established." + }, + "needs_human_before": false, + "task_before_public_metadata": { + "id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "status": "wait", + "needs_human": false, + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved." + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/canonical-receiver.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/canonical-receiver.json new file mode 100644 index 00000000..61314846 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/canonical-receiver.json @@ -0,0 +1,173 @@ +{ + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "derived_commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "key-cape", + "commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "status": "applied", + "counts": { + "created": 0, + "updated": 44, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 1, + "external_edges": 8 + }, + "created": [], + "updated": [ + "ADHOC-2026-09-05", + "ADHOC-2026-09-07", + "ADHOC-2026-09-27", + "KEY-WP-0001", + "KEY-WP-0002", + "KEY-WP-0003", + "KEY-WP-0004", + "KEY-WP-0005", + "KEY-WP-0006", + "KEY-WP-0007", + "KEY-WP-0008", + "KEY-WP-0009", + "KEY-WP-0010", + "KEY-WP-0011", + "KEY-WP-0012", + "KEY-WP-0013", + "KEY-WP-0014", + "KEY-WP-0015", + "KEY-WP-0016", + "KEY-WP-0017", + "KEY-WP-0018", + "KEY-WP-0019", + "KEY-WP-0020", + "KEY-WP-0021", + "KEY-WP-0022", + "KEY-WP-0023", + "KEY-WP-0024", + "KEY-WP-0025", + "KEY-WP-0026", + "KEY-WP-0027", + "KEY-WP-0028", + "KEY-WP-0029", + "KEY-WP-0030", + "KEY-WP-0031", + "KEY-WP-0032", + "KEY-WP-0033", + "KEY-WP-0034", + "KEY-WP-0035", + "KEY-WP-0036", + "KEY-WP-0037", + "KEY-WP-0038", + "KEY-WP-0039", + "KEY-WP-0040", + "KEY-WP-0041" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "KEY-WP-0010", + "from": "KEY-WP-0010", + "target": "RAILIANCE-WP-0027", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "KEY-WP-0038", + "from": "KEY-WP-0038-T03", + "target": "KEY-WP-0038-T02", + "relationship_type": "starts_after", + "from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9", + "from_task_id": "40ddaa29-7245-508c-ad48-ddc063e25115", + "to_workplan_id": null, + "to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" + }, + { + "workplan": "KEY-WP-0038", + "from": "KEY-WP-0038-T02", + "target": "KEY-WP-0038-T01", + "relationship_type": "starts_after", + "from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9", + "from_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30", + "to_workplan_id": null, + "to_task_id": "5f019d13-1fa3-5b88-a27a-75a3339ca96e" + }, + { + "workplan": "KEY-WP-0038", + "from": "KEY-WP-0038-T02", + "target": "NK-WP-0048-T06", + "relationship_type": "starts_after", + "from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9", + "from_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30", + "to_workplan_id": null, + "to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" + }, + { + "workplan": "KEY-WP-0039", + "from": "KEY-WP-0039-T02", + "target": "KEY-WP-0038-T03", + "relationship_type": "starts_after", + "from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "to_workplan_id": null, + "to_task_id": "40ddaa29-7245-508c-ad48-ddc063e25115" + }, + { + "workplan": "KEY-WP-0039", + "from": "KEY-WP-0039-T02", + "target": "KEY-WP-0039-T01", + "relationship_type": "starts_after", + "from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "to_workplan_id": null, + "to_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8" + }, + { + "workplan": "KEY-WP-0039", + "from": "KEY-WP-0039-T02", + "target": "KEY-WP-0038-T02", + "relationship_type": "starts_after", + "from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "to_workplan_id": null, + "to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30" + }, + { + "workplan": "KEY-WP-0007", + "from": "KEY-WP-0007", + "target": "KEY-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "24db4178-705c-5b09-b7db-ee7a345d71a0", + "from_task_id": null, + "to_workplan_id": "711a8303-69aa-5db6-9043-92530c6dc81b", + "to_task_id": null + }, + { + "workplan": "KEY-WP-0009", + "from": "KEY-WP-0009", + "target": "KEY-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "c1a9b1cc-2ff0-566a-b544-1a2ef967fc0d", + "from_task_id": null, + "to_workplan_id": "d8d20d8f-61e0-54e2-9a88-a3958f0b6f2d", + "to_task_id": null + } + ], + "refused": [], + "notes": [] + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/canonical-reconcile.py b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/canonical-reconcile.py new file mode 100644 index 00000000..d6233ca6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/canonical-reconcile.py @@ -0,0 +1,40 @@ +from pathlib import Path +import hashlib,json,subprocess,urllib.request,urllib.error,yaml,re +root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1');base='http://127.0.0.1:8000';head='d803e9282a47b49daf930fb905312fa5104c0ace' +def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode() +assert git('rev-parse','HEAD').strip()==head +assert git('ls-remote','origin','refs/heads/main').split()[0]==head +assert not git('status','--porcelain=v1') +def get(path): + with urllib.request.urlopen(base+path,timeout=30) as response:return json.load(response) +health=get('/state/health');assert (health['instance_role'],health['instance_label'])==('primary','railiance01') +request_path='/repos/key-cape/work-record-projection/reconcile';body={'expected_commit':head,'acknowledge_retirements':False} +digest=hashlib.sha256(json.dumps({'method':'POST','path':request_path,'body':body},sort_keys=True,separators=(',',':')).encode()).hexdigest() +request=urllib.request.Request(base+request_path,data=json.dumps(body).encode(),method='POST',headers={'Content-Type':'application/json','Idempotency-Key':'rmgr-projection:v2:'+digest,'X-StateHub-Source-Agent':'custodian-codex'}) +try: + with urllib.request.urlopen(request,timeout=120) as response:receipt=json.load(response) +except urllib.error.HTTPError as error: + raw=error.read();(out/'canonical-receiver-refusal.json').write_bytes(raw);raise +(out/'canonical-receiver.json').write_text(json.dumps(receipt,indent=2)+'\n') +assert receipt['expected_commit']==receipt['derived_commit']==head +assert (receipt['instance_role'],receipt['instance_label'])==('primary','railiance01') +assert receipt['outcome']['status'] in ('applied','noop'),receipt['outcome']['status'] +snapshot=get('/repos/key-cape/work-record-projection/snapshot');baseline=json.loads((out/'baseline.json').read_text()) +planid=baseline['frontmatter']['state_hub_workstream_id'];plan=next(w for w in snapshot['workplans'] if w['id']==planid) +rows=[] +for block in baseline['task_blocks']: + task=next(t for t in snapshot['tasks'] if t['id']==block['state_hub_task_id']) + assert task['status']==block['status'];rows.append({'record_id':block['id'],'state_hub_task_id':task['id'],'status':task['status'],'workplan_id':task.get('workplan_id'),'depends_on_source':block.get('depends_on',[])}) +current=(root/baseline['path']).read_text();assert current==(out/'KEY39.proposed.md').read_text() +actual_blocks=[yaml.safe_load(t) for t in re.findall(r'^```task\n(.*?)^```',current,re.M|re.S)] +assert actual_blocks==baseline['task_blocks_after'] +for before,after in zip(baseline['task_blocks'],actual_blocks): + expected=dict(before) + if before['id']=='KEY-WP-0039-T02':expected['blocking_reason']=baseline['only_task_field_delta']['new'] + assert after==expected +task_after=get('/tasks/03da4c38-1dac-53b1-8b0b-e697df48e9d3') +assert task_after.get('needs_human') is False +assert task_after.get('blocking_reason')==baseline['only_task_field_delta']['new'] +verification={'commit':head,'receiver_status':receipt['outcome']['status'],'acknowledge_retirements':False,'instance_role':receipt['instance_role'],'instance_label':receipt['instance_label'],'workplan':{'id':planid,'status':plan['status'],'record_id':'KEY-WP-0039'},'task_bindings':rows,'all_other_task_fields_unchanged':True,'only_task_field_delta':baseline['only_task_field_delta'],'needs_human':False,'primary_task_blocking_reason':task_after['blocking_reason'],'frontmatter_unchanged':True,'only_committed_path':baseline['path'],'remote_forge_matches':True,'owner_worktree_and_index_clean':not git('status','--porcelain=v1'),'normal_sync_refusal':'unrelated grandfathered ADHOC-2026-09-27-T02 UUID; not modified','native_acceptance_inferred':False} +assert plan['status']==baseline['frontmatter']['status'] +(out/'final-verification.json').write_text(json.dumps(verification,indent=2)+'\n');print(json.dumps(verification,indent=2)) diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/commit.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/commit.json new file mode 100644 index 00000000..25c9aed5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/commit.json @@ -0,0 +1,12 @@ +{ + "commit": "d803e9282a47b49daf930fb905312fa5104c0ace", + "parent": "26ec9bb9b195ad377448570d6b161fd682311a0b", + "paths": [ + "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md" + ], + "normal_hook": "scanner8.30.1 clean", + "only_task_field_delta": "KEY-WP-0039-T02 blocking_reason suffix; all other fields unchanged", + "native_or_credential_action": false, + "needs_human": false, + "needs_human_preserved": true +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/.custodian-brief.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/.custodian-brief.md new file mode 100644 index 00000000..d3b36c3b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/.custodian-brief.md @@ -0,0 +1,37 @@ + +# Custodian Brief — key-cape + +**Domain:** infotech +**Last synced:** 2026-10-17 02:16 UTC +**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* + +## Active Workstreams + +### Negative-check tokens for the secrets-engine OpenBao JWT role +Progress: 2/3 done | workplan_id: `09c3bddf-6058-5603-8488-cccdb7be4f86` + +**Open tasks:** +- ! T01 — Define the probe registrations `03da4c38` + *(wait: Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.)* + +### Forgejo as a NetKingdom OIDC client, so the forge can close +Progress: 2/4 done | workplan_id: `098e3e2c-a138-560e-b2d3-3a15eb49d8a9` + +**Open tasks:** +- ! T01 — Define what Forgejo receives `ddcfb482` + *(wait: Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout.)* +- ! T02 — Register the client and hand over its secret `40ddaa29` + *(wait: Claims are verified against the registered client.)* + +## Inbox Hygiene + +**Stale unread:** 2 message(s) older than 3 day(s) — triage at session start. +**Missing thread_id:** 2 unread message(s) lack supersession chains. + +--- +## MCP Orientation (when available) + +If the state-hub MCP server is reachable, call: +`get_domain_summary("infotech")` +This provides richer cross-domain context. +If the MCP call fails, use this file as your orientation source. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/WORK-RECORDS.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/WORK-RECORDS.md new file mode 100644 index 00000000..d86b29bb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/WORK-RECORDS.md @@ -0,0 +1,224 @@ +# Work Records — key-cape + +> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record +> stage 3). Do not edit by hand — edit the source file/block listed for +> each record and re-run fix-consistency to refresh this index. Archived +> workplans are omitted; closed decisions/intakes/engagements stay listed +> so recently-resolved work is still visible. [auto] + +| Kind | ID | Status | Lane | Source | +| --- | --- | --- | --- | --- | +| workplan | ADHOC-2026-09-05 | finished | — | workplans/ADHOC-2026-09-05.md | +| workplan | ADHOC-2026-09-07 | finished | — | workplans/ADHOC-2026-09-07.md | +| workplan | ADHOC-2026-09-27 | finished | — | workplans/ADHOC-2026-09-27.md | +| workplan | KEY-WP-0001 | finished | — | workplans/KEY-WP-0001-keycape-implementation.md | +| workplan | KEY-WP-0002 | finished | — | workplans/KEY-WP-0002-container-image-gitea.md | +| workplan | KEY-WP-0003 | finished | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| workplan | KEY-WP-0004 | finished | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| workplan | KEY-WP-0005 | finished | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| workplan | KEY-WP-0006 | finished | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| workplan | KEY-WP-0007 | finished | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| workplan | KEY-WP-0008 | finished | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| workplan | KEY-WP-0009 | finished | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| workplan | KEY-WP-0010 | finished | — | workplans/KEY-WP-0010-openbao-operator-loopback-callback.md | +| workplan | KEY-WP-0011 | finished | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| workplan | KEY-WP-0012 | finished | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| workplan | KEY-WP-0013 | finished | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| workplan | KEY-WP-0014 | finished | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| workplan | KEY-WP-0015 | finished | — | workplans/KEY-WP-0015-scope-intent-assessment.md | +| workplan | KEY-WP-0016 | finished | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| workplan | KEY-WP-0017 | finished | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| workplan | KEY-WP-0018 | finished | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| workplan | KEY-WP-0019 | finished | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| workplan | KEY-WP-0020 | finished | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| workplan | KEY-WP-0021 | finished | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| workplan | KEY-WP-0022 | finished | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| workplan | KEY-WP-0023 | finished | — | workplans/KEY-WP-0023-live-migration-proof.md | +| workplan | KEY-WP-0024 | finished | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md | +| workplan | KEY-WP-0025 | finished | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| workplan | KEY-WP-0026 | finished | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| workplan | KEY-WP-0027 | finished | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| workplan | KEY-WP-0028 | finished | — | workplans/KEY-WP-0028-browser-client-field-validation.md | +| workplan | KEY-WP-0029 | finished | — | workplans/KEY-WP-0029-scope-reassessment.md | +| workplan | KEY-WP-0030 | finished | — | workplans/KEY-WP-0030-tenant-precondition-guard.md | +| workplan | KEY-WP-0031 | finished | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md | +| workplan | KEY-WP-0032 | finished | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md | +| workplan | KEY-WP-0033 | finished | — | workplans/KEY-WP-0033-vergabe-fresh-login.md | +| workplan | KEY-WP-0034 | finished | — | workplans/KEY-WP-0034-account-recovery.md | +| workplan | KEY-WP-0035 | finished | — | workplans/KEY-WP-0035-optional-mfa.md | +| workplan | KEY-WP-0036 | finished | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| workplan | KEY-WP-0037 | finished | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| workplan | KEY-WP-0038 | active | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| workplan | KEY-WP-0039 | active | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| workplan | KEY-WP-0040 | finished | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| workplan | KEY-WP-0041 | finished | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | +| task | ADHOC-2026-09-05-T01 | done | — | workplans/ADHOC-2026-09-05.md | +| task | ADHOC-2026-09-07-T01 | done | — | workplans/ADHOC-2026-09-07.md | +| task | ADHOC-2026-09-27-T01 | done | — | workplans/ADHOC-2026-09-27.md | +| task | ADHOC-2026-09-27-T02 | done | — | workplans/ADHOC-2026-09-27.md | +| task | KEY-WP-0001-T01 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T02 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T03 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T04 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T05 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T06 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T07 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T08 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T09 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T10 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T11 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T12 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T13 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T14 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T15 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T16 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T17 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T18 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T19 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T20 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T21 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T22 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0001-T23 | done | — | workplans/KEY-WP-0001-keycape-implementation.md | +| task | KEY-WP-0002-T01 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T02 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T03 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T04 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T05 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0002-T06 | done | — | workplans/KEY-WP-0002-container-image-gitea.md | +| task | KEY-WP-0003-T01 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T02 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T03 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T04 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0003-T05 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md | +| task | KEY-WP-0004-T01 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T02 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T03 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T04 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T05 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T06 | cancel | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0004-T07 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md | +| task | KEY-WP-0005-T01 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| task | KEY-WP-0005-T02 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| task | KEY-WP-0005-T03 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | +| task | KEY-WP-0006-T01 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T02 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T03 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T04 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T05 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0007-T01 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| task | KEY-WP-0007-T02 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| task | KEY-WP-0007-T03 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md | +| task | KEY-WP-0008-T01 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0008-T02 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0008-T03 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0008-T04 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md | +| task | KEY-WP-0009-T01 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0009-T02 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0009-T03 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0009-T04 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md | +| task | KEY-WP-0010-T01 | done | — | workplans/KEY-WP-0010-openbao-operator-loopback-callback.md | +| task | KEY-WP-0011-T01 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| task | KEY-WP-0011-T02 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| task | KEY-WP-0011-T03 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md | +| task | KEY-WP-0012-T01 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| task | KEY-WP-0012-T02 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| task | KEY-WP-0012-T03 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md | +| task | KEY-WP-0013-T01 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T02 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T03 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T04 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T05 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T06 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0013-T07 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md | +| task | KEY-WP-0014-T01 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0014-T02 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0014-T03 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0014-T04 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md | +| task | KEY-WP-0015-T01 | done | — | workplans/KEY-WP-0015-scope-intent-assessment.md | +| task | KEY-WP-0016-T01 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0016-T02 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0016-T03 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0016-T04 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md | +| task | KEY-WP-0017-T01 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0017-T02 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0017-T03 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0017-T04 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md | +| task | KEY-WP-0018-T01 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| task | KEY-WP-0018-T02 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| task | KEY-WP-0018-T03 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md | +| task | KEY-WP-0019-T01 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T02 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T03 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T04 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0019-T05 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md | +| task | KEY-WP-0020-T01 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| task | KEY-WP-0020-T02 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| task | KEY-WP-0020-T03 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md | +| task | KEY-WP-0021-T01 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| task | KEY-WP-0021-T02 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| task | KEY-WP-0021-T03 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md | +| task | KEY-WP-0022-T01 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T02 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T03 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T04 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0022-T05 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md | +| task | KEY-WP-0023-T01 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T02 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T03 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T04 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0023-T05 | done | — | workplans/KEY-WP-0023-live-migration-proof.md | +| task | KEY-WP-0024-T01 | done | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md | +| task | KEY-WP-0024-T02 | done | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md | +| task | KEY-WP-0025-T01 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0025-T02 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0025-T03 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0025-T04 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md | +| task | KEY-WP-0026-T01 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T02 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T03 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T04 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T05 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0026-T06 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md | +| task | KEY-WP-0027-T01 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0027-T02 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0027-T03 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0027-T04 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md | +| task | KEY-WP-0028-T01 | done | — | workplans/KEY-WP-0028-browser-client-field-validation.md | +| task | KEY-WP-0028-T02 | done | — | workplans/KEY-WP-0028-browser-client-field-validation.md | +| task | KEY-WP-0029-T01 | done | — | workplans/KEY-WP-0029-scope-reassessment.md | +| task | KEY-WP-0029-T02 | done | — | workplans/KEY-WP-0029-scope-reassessment.md | +| task | KEY-WP-0029-T03 | done | — | workplans/KEY-WP-0029-scope-reassessment.md | +| task | KEY-WP-0030-T01 | done | — | workplans/KEY-WP-0030-tenant-precondition-guard.md | +| task | KEY-WP-0030-T02 | done | — | workplans/KEY-WP-0030-tenant-precondition-guard.md | +| task | KEY-WP-0031-T01 | done | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md | +| task | KEY-WP-0031-T02 | done | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md | +| task | KEY-WP-0032-T01 | done | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md | +| task | KEY-WP-0032-T02 | done | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md | +| task | KEY-WP-0033-T01 | done | — | workplans/KEY-WP-0033-vergabe-fresh-login.md | +| task | KEY-WP-0033-T02 | done | — | workplans/KEY-WP-0033-vergabe-fresh-login.md | +| task | KEY-WP-0034-T01 | done | — | workplans/KEY-WP-0034-account-recovery.md | +| task | KEY-WP-0034-T02 | done | — | workplans/KEY-WP-0034-account-recovery.md | +| task | KEY-WP-0035-T01 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T02 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T03 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T04 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0035-T05 | done | — | workplans/KEY-WP-0035-optional-mfa.md | +| task | KEY-WP-0036-T01 | done | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| task | KEY-WP-0036-T02 | cancel | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| task | KEY-WP-0036-T03 | done | — | workplans/KEY-WP-0036-platform-factor-recovery.md | +| task | KEY-WP-0037-T01 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0037-T02 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0037-T03 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0037-T04 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md | +| task | KEY-WP-0038-T01 | done | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0038-T02 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0038-T03 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0038-T04 | cancel | — | workplans/KEY-WP-0038-forgejo-oidc-client.md | +| task | KEY-WP-0039-T01 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0039-T02 | wait | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0039-T03 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md | +| task | KEY-WP-0040-T01 | done | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0040-T02 | done | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md | +| task | KEY-WP-0041-T01 | done | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | +| task | KEY-WP-0041-T02 | done | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md | +| intake | KEY-IN-0001 | closed | — | intakes/intakes.md | diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/originals.json b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/originals.json new file mode 100644 index 00000000..f0239b7d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/originals.json @@ -0,0 +1,232 @@ +{ + ".custodian-brief.md": { + "sha256": "96d84539b3a92786bff37fcc70161adec4648b9547287e52e6b817bc080a080a", + "mode": 436, + "index": "100644 d3b36c3bffbeb922769a2a788e35b4113fccaa3f 0\t.custodian-brief.md\n" + }, + "WORK-RECORDS.md": { + "sha256": "431842547bd82750f284f8c1c9f519892f1bb1c1a684b9b1e38adc6fdfdc299d", + "mode": 420, + "index": "100644 d86b29bbc480acfa6d12d5052039578d7d4e380c 0\tWORK-RECORDS.md\n" + }, + "workplans/ADHOC-2026-09-05.md": { + "sha256": "76123d22d0c1efc51212f8914e386f078b3c6d908959af6978ae73874e492b04", + "mode": 420, + "index": "100644 f7e78675df62c9b48e29f882fa99298d6d8ce7f1 0\tworkplans/ADHOC-2026-09-05.md\n" + }, + "workplans/ADHOC-2026-09-07.md": { + "sha256": "c335ebfe91dad291277e8f17034ac8f785b3bc767f281f205c446c08f03d7978", + "mode": 420, + "index": "100644 17401073c053c9710e6e680243bbd313b08d1fc1 0\tworkplans/ADHOC-2026-09-07.md\n" + }, + "workplans/ADHOC-2026-09-27.md": { + "sha256": "2794d1940fc3c5e4a2df57de9d4b742fa6aa8602b67f1d3177c0d96bc5aaf314", + "mode": 420, + "index": "100644 0b755547aa7d5589f0898df2f102a45b6aa24838 0\tworkplans/ADHOC-2026-09-27.md\n" + }, + "workplans/KEY-WP-0001-keycape-implementation.md": { + "sha256": "29a2bf4892b7185e28e9126a3d8311d0fed34a876f416a3e75c605915fde0969", + "mode": 420, + "index": "100644 74334411cd056e7c7253e84f1c4b40c42eb13de3 0\tworkplans/KEY-WP-0001-keycape-implementation.md\n" + }, + "workplans/KEY-WP-0002-container-image-gitea.md": { + "sha256": "76fee187185b30219bc257ccc9b587661df4489574a2bb57703a365a5d803cec", + "mode": 420, + "index": "100644 418f477c0ede13d7c003d744abf234347f5d5e31 0\tworkplans/KEY-WP-0002-container-image-gitea.md\n" + }, + "workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md": { + "sha256": "5e949c696e587540d305b682eb1f8390ec544bbec6a566be8d04e73fb1b254ca", + "mode": 420, + "index": "100644 9896c67d78f0efdd03325535eb41295250be3a19 0\tworkplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md\n" + }, + "workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md": { + "sha256": "260ba7d3116db3bc5a72e5cd8b9984137e7e372c8128d21073614dbd948c10c2", + "mode": 420, + "index": "100644 a50e910558ee43073c92cb27865a1aceac990dd5 0\tworkplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md\n" + }, + "workplans/KEY-WP-0005-iam-profile-core-claims.md": { + "sha256": "c6702b644cdd046c0d3bba8ba8837050826e4dc3d855bd73327ae94218ba5b57", + "mode": 420, + "index": "100644 648d2405699dcf25b3121df6c46d871597100d7b 0\tworkplans/KEY-WP-0005-iam-profile-core-claims.md\n" + }, + "workplans/KEY-WP-0006-client-credentials-service-tokens.md": { + "sha256": "b3227849978c95cad89e02afb74bf2217b6188ae65c5b0af6a21703d65477701", + "mode": 420, + "index": "100644 632c4c2cad39cbde55c2ae1f2ba5ecf2f445b1a0 0\tworkplans/KEY-WP-0006-client-credentials-service-tokens.md\n" + }, + "workplans/KEY-WP-0007-user-engine-portal-oidc-client.md": { + "sha256": "5408ff6dfeb58a8a7756b48fe4f54f7c7b98d7940cb9aaa5c7864f1247e58150", + "mode": 420, + "index": "100644 f1b04e1a61b398426c23d8d9ca655b3937d7bd10 0\tworkplans/KEY-WP-0007-user-engine-portal-oidc-client.md\n" + }, + "workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md": { + "sha256": "bfc9b2118f0712e92a86bbb0f14a2857fafc3a42a3c14c0e2535002aaa57a2f5", + "mode": 420, + "index": "100644 438ab5480de3bd66d75b6ec7e09639d1b93e3e5f 0\tworkplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md\n" + }, + "workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md": { + "sha256": "d1daac9815930104a392ea96c14922e34f947720a31d1b0ac45a770997e0f476", + "mode": 420, + "index": "100644 c9e585db65424c7c4e3b5adf37a68de2bdfbdc1c 0\tworkplans/KEY-WP-0009-provider-capabilities-and-service-identities.md\n" + }, + "workplans/KEY-WP-0010-openbao-operator-loopback-callback.md": { + "sha256": "41d28a16bef9313b776c5c6305316a9e7d4f5528b7fb7427b8384a0ed0ad35c4", + "mode": 420, + "index": "100644 81614eeb8c98205d6e5eb3201da10e36b76a21e9 0\tworkplans/KEY-WP-0010-openbao-operator-loopback-callback.md\n" + }, + "workplans/KEY-WP-0011-live-secret-exposure-recovery.md": { + "sha256": "3c5458180fe81a04e357f8db737e4287f79640b5ab02682bcadc595b73f846cd", + "mode": 420, + "index": "100644 f7b906f552f6e3279895a90afbe216b6e78ee5ff 0\tworkplans/KEY-WP-0011-live-secret-exposure-recovery.md\n" + }, + "workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md": { + "sha256": "68d34d75c914e7d2d8177000e800d6372cccda2b850c03ab85da42b90a66b91c", + "mode": 420, + "index": "100644 0f3deb626e5de9a9f898754ef81d3f2ee5282792 0\tworkplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md\n" + }, + "workplans/KEY-WP-0013-approval-engine-resource-audience.md": { + "sha256": "55c88bf4ed9a456db59b319bbaf960dfe6f4147437e339f950e0583d7582057a", + "mode": 420, + "index": "100644 7d77aee3caf3d79fe6d6c1369249c6d94e0ae123 0\tworkplans/KEY-WP-0013-approval-engine-resource-audience.md\n" + }, + "workplans/KEY-WP-0014-native-credential-lane-handoff.md": { + "sha256": "22aac43ae5039aebd11f9f0aa57a882ad009ad6ed119ec08eb85b32a5af48cd7", + "mode": 420, + "index": "100644 bacf04c07f59125a90c519d3c306a7414cb9a1a3 0\tworkplans/KEY-WP-0014-native-credential-lane-handoff.md\n" + }, + "workplans/KEY-WP-0015-scope-intent-assessment.md": { + "sha256": "60b9f2265eff880d916c3ee8dba36aec9984ec310804de776cebf89cc98fdb95", + "mode": 420, + "index": "100644 82b952a216346ffb7f5cab413623fe4a88f7700e 0\tworkplans/KEY-WP-0015-scope-intent-assessment.md\n" + }, + "workplans/KEY-WP-0016-authorization-code-protocol-hardening.md": { + "sha256": "ed48128a1cb51cbe3d154727bafd5a5ffb3b5b618c20649b2a0e7db154cf3a0c", + "mode": 420, + "index": "100644 52839bd0f83d97656c8ad97a8ffc338d9019239a 0\tworkplans/KEY-WP-0016-authorization-code-protocol-hardening.md\n" + }, + "workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md": { + "sha256": "8700e0e5aa07dcd7f651e792cca12a0330b7ee9d2f6064b4cc3fcf4c7d509340", + "mode": 420, + "index": "100644 00eb03971aae75724afbf7b8a4946ce6b7eda3af 0\tworkplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md\n" + }, + "workplans/KEY-WP-0018-export-completeness-evidence.md": { + "sha256": "15026b708d116f803b0b2fa43f30a4dbced76e4d8137b5fcb5b4275e593d7c7b", + "mode": 420, + "index": "100644 325fc483d62dbad53b92b986de56112168a92b4a 0\tworkplans/KEY-WP-0018-export-completeness-evidence.md\n" + }, + "workplans/KEY-WP-0019-upstream-provider-token-verification.md": { + "sha256": "0d2d8b5bda1e0d333935812d8d3749cb7b0c5523a3987d1b26185f9f6d0f0fa2", + "mode": 420, + "index": "100644 dfeb92296f46d4ce2ac7df1fedcc61e9ccc5d6b3 0\tworkplans/KEY-WP-0019-upstream-provider-token-verification.md\n" + }, + "workplans/KEY-WP-0020-migration-contract-preservation.md": { + "sha256": "cc842d15bf42ddc4d0f4056bc19b3176cab2c5f3c1a2fe6d648e5dbd077be8b3", + "mode": 420, + "index": "100644 7b6f3be976293469dee2180c10e16f313876d640 0\tworkplans/KEY-WP-0020-migration-contract-preservation.md\n" + }, + "workplans/KEY-WP-0021-snapshot-attribute-validation.md": { + "sha256": "b603248693073a9b9b25b4d0a4147d8d88c16a07dc9936e48fa1f3c76fe8bab6", + "mode": 420, + "index": "100644 1f6efeb32a6eb8fcbc52f4d3b591e2fc6b9e006d 0\tworkplans/KEY-WP-0021-snapshot-attribute-validation.md\n" + }, + "workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md": { + "sha256": "8c135e84be81de31b4949f52ac28dccd404f2e4c3eaa576d733bd50160427a53", + "mode": 420, + "index": "100644 7c69629ecd20d0158aa1332d9d1442b796f6a7af 0\tworkplans/KEY-WP-0022-replacement-harness-and-external-conformance.md\n" + }, + "workplans/KEY-WP-0023-live-migration-proof.md": { + "sha256": "e581cd11f3905805b5e8ba8e2ede85092577c7d086e1761f2a2d033fdead4e47", + "mode": 420, + "index": "100644 3e7c83cb12e7fc31e682cf3c27f4c4767ded2905 0\tworkplans/KEY-WP-0023-live-migration-proof.md\n" + }, + "workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md": { + "sha256": "af7842fa97263bfd46a4b5ae84c5c1ecddb06ab9623586e0292dcc93099fea35", + "mode": 420, + "index": "100644 baa0bbd195a92708f2befd027b938fcd1812ced7 0\tworkplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md\n" + }, + "workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md": { + "sha256": "0addb34e6d85e376c6a5db7afba597b6f268afa98b86c8c60add9be174dd1b26", + "mode": 420, + "index": "100644 a53e646761178748e8c4b00dcde888698447a948 0\tworkplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md\n" + }, + "workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md": { + "sha256": "c1bc0de47511ae49102009a172c84d9d5d64e829a18c8d40564fe817935563b3", + "mode": 420, + "index": "100644 1f96a2ac3ac939c4e6e0f66172477cf73db04e51 0\tworkplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md\n" + }, + "workplans/KEY-WP-0027-rollout-readiness-and-live-state.md": { + "sha256": "f559eae61d433d451f80ea575254bb14a47f2485267b412e42d524b434bb1b59", + "mode": 420, + "index": "100644 57e01b2db033847cc786504b8795e37112d988a7 0\tworkplans/KEY-WP-0027-rollout-readiness-and-live-state.md\n" + }, + "workplans/KEY-WP-0028-browser-client-field-validation.md": { + "sha256": "fbd51422fa5e6449000e4520e6639f6135c7393165a5ae02b1e92892edba0480", + "mode": 420, + "index": "100644 46b0672fea90cd7cb672f7d119706c240474f8ca 0\tworkplans/KEY-WP-0028-browser-client-field-validation.md\n" + }, + "workplans/KEY-WP-0029-scope-reassessment.md": { + "sha256": "3241f3b94db9cad5e3770cf1fedc5c211288357e4f69b5ba9da57065094bdf0e", + "mode": 420, + "index": "100644 392f97750f6cb8b12868a6fd016c7d396b58477e 0\tworkplans/KEY-WP-0029-scope-reassessment.md\n" + }, + "workplans/KEY-WP-0030-tenant-precondition-guard.md": { + "sha256": "c94c3afc7fb4270dfc18f3fe8a32946c524e4a6ba2d02a3d8fda69c9b8441af0", + "mode": 420, + "index": "100644 0f74e0d5a037eec64b11b57c67b02057a3b0bfef 0\tworkplans/KEY-WP-0030-tenant-precondition-guard.md\n" + }, + "workplans/KEY-WP-0031-gate-house-tenant-ruling.md": { + "sha256": "0abb135367f0366aa071769c4bd76fdeb321d810ec75ad3b58d718734f78382b", + "mode": 420, + "index": "100644 d2ede9d494927976ccb9ba6a5aeaf1ed21c30837 0\tworkplans/KEY-WP-0031-gate-house-tenant-ruling.md\n" + }, + "workplans/KEY-WP-0032-principal-type-provenance-guard.md": { + "sha256": "6a5cd483a40b9477227b02156c42b3469094dac145b33727b336118898aa782a", + "mode": 420, + "index": "100644 a04f52f817a51efc1d0d0ecb99efacea8dc53a17 0\tworkplans/KEY-WP-0032-principal-type-provenance-guard.md\n" + }, + "workplans/KEY-WP-0033-vergabe-fresh-login.md": { + "sha256": "c01bb51cf2dcad06ea846040dddef09845041fa7271cd7015e5a34a49e60bb8f", + "mode": 420, + "index": "100644 5b478f953890339aec796c11a536af0ec3238789 0\tworkplans/KEY-WP-0033-vergabe-fresh-login.md\n" + }, + "workplans/KEY-WP-0034-account-recovery.md": { + "sha256": "7db9adcd418ccabfbce672aec283249a6e374212492f0349e8c47a568f130eda", + "mode": 420, + "index": "100644 eb58f99bc26e3c3ac33230dfe432c8b1fabe47ce 0\tworkplans/KEY-WP-0034-account-recovery.md\n" + }, + "workplans/KEY-WP-0035-optional-mfa.md": { + "sha256": "c60ea8d0c195686c0d2317f657aad2de27c9b9432d311627c0e1d06124cabb49", + "mode": 420, + "index": "100644 29eb711f656c91808e91f3981a9168c3a0ebc253 0\tworkplans/KEY-WP-0035-optional-mfa.md\n" + }, + "workplans/KEY-WP-0036-platform-factor-recovery.md": { + "sha256": "5ceb08fe5789b4f02550bb04fcaf8eeb25a43ac3f506dfc62d60a513bcd0013f", + "mode": 420, + "index": "100644 fd296bc05a6e565b0ef6e88a9ae3daa7c9f81786 0\tworkplans/KEY-WP-0036-platform-factor-recovery.md\n" + }, + "workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md": { + "sha256": "3a90a0c460fc439570820b96d1ddc3b7f8646c557ca3a8aa27c6d546bb1a845a", + "mode": 420, + "index": "100644 ad7fed77a27aa8487693ad6fcf342502f418427a 0\tworkplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md\n" + }, + "workplans/KEY-WP-0038-forgejo-oidc-client.md": { + "sha256": "dc8d88d462799e7e6e44da759a973d52e44102a271969a5daaa9d30ec015b064", + "mode": 420, + "index": "100644 8cd5bc66f7b0115eec7813d6eec994a1bb618668 0\tworkplans/KEY-WP-0038-forgejo-oidc-client.md\n" + }, + "workplans/KEY-WP-0039-openbao-negative-probe-tokens.md": { + "sha256": "517f9526d574ce3faafad18837e0039db8f3f7990eac3e34a1678cdd7307a9e1", + "mode": 420, + "index": "100644 4398517afe04e3b0723722af37b434fa8d2209ab 0\tworkplans/KEY-WP-0039-openbao-negative-probe-tokens.md\n" + }, + "workplans/KEY-WP-0040-mfa-attended-run-findings.md": { + "sha256": "98b23d8810bff047067169232213cb313ecc14ff79751224842723afe808fbd2", + "mode": 420, + "index": "100644 8e8730c8e7733b13d6a9f42689a9d8e407769e81 0\tworkplans/KEY-WP-0040-mfa-attended-run-findings.md\n" + }, + "workplans/KEY-WP-0041-sign-in-diagnosis-page.md": { + "sha256": "7e44f46acd6b3942c1852b49e0d64b205dfd7d9bb378aed5241b1bf22d7ce88a", + "mode": 420, + "index": "100644 56fd05819e3acd37b8e6cb468ec3d62307f1ae7a 0\tworkplans/KEY-WP-0041-sign-in-diagnosis-page.md\n" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-05.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-05.md new file mode 100644 index 00000000..f7e78675 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-05.md @@ -0,0 +1,27 @@ +--- +id: ADHOC-2026-09-05 +type: workplan +title: "Repository review and layer declaration" +domain: infotech +repo: key-cape +status: finished +owner: codex +created: "2026-09-05" +updated: "2026-09-05" +state_hub_workstream_id: "6f5a12f0-5dac-5e02-bbd9-9ab2263264ba" +--- + +## Resolve declaration intake and normalize repository records + +```task +id: ADHOC-2026-09-05-T01 +status: done +priority: low +state_hub_task_id: "54c89abe-2539-550d-bc3a-649af8d4580a" +``` + +Reviewed KEY-IN-0001 against local security-layer-model_v0.4. Declared Tooling +and the authentication/authorization boundary in INTENT.md and closed the intake. +Normalized two legacy completed workplan frontmatter statuses to finished. +Ignored the generated .repo-manager workstation cache. Existing task statuses +already conform to the State Hub task lifecycle canon. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-07.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-07.md new file mode 100644 index 00000000..17401073 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-07.md @@ -0,0 +1,36 @@ +--- +id: ADHOC-2026-09-07 +type: workplan +title: "Document the KEY-WP-0016 authorization-code bindings for consumers" +domain: infotech +repo: key-cape +status: finished +owner: claude +created: "2026-09-07" +updated: "2026-09-07" +state_hub_workstream_id: "c2fdc5cc-89be-561d-b832-f0b3019ea1c7" +--- + +## Publish the consumer-facing rollout note + +```task +id: ADHOC-2026-09-07-T01 +status: done +priority: medium +state_hub_task_id: "2187cef2-07c0-5ac1-89ca-3bb25a160fa4" +``` + +KEY-WP-0016 changed `/token` and `/userinfo` behaviour without a consumer-facing +note; no document in `docs/` mentioned `redirect_uri` at all, so the change would +have reached a deployment silently. + +Published `docs/authorization-code-bindings.md` stating what an exchange must +send, who is affected and how to roll out. Verified the blast radius rather than +assuming it: all five browser registrations in `config/dev-config.yaml` +(`openbao-admin`, `demo-app`, `netkingdom-bootstrap-console`, +`user-engine-portal`, `coulomb-social`) are `clientType: public` with +`grantTypes: ["authorization_code"]`, so the grant-type and confidential-client +bindings cannot affect them and only the `redirect_uri` requirement can. Named +the realistic failure — a client that sends `redirect_uri` to `/authorize` but +omits it at `/token` — and said plainly that it has not been observed against a +live consumer. Referenced the note from SCOPE.md. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-27.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-27.md new file mode 100644 index 00000000..0b755547 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/ADHOC-2026-09-27.md @@ -0,0 +1,100 @@ +--- +id: ADHOC-2026-09-27 +type: workplan +title: "Fresh MFA no longer backdated to a stale kc_login cookie (INFD-IN-0005)" +domain: infotech +repo: key-cape +status: finished +owner: claude +created: "2026-09-27" +updated: "2026-09-27" +state_hub_workstream_id: "5217dd06-73cb-50f8-ba64-528e421b29de" +--- + +## Stop completeAuthorization from reusing a stale prior session's IssuedAt after real MFA + +```task +id: ADHOC-2026-09-27-T01 +status: done +priority: high +state_hub_task_id: "ce98e4af-cfaa-5620-8f21-15ade18ee9e8" +``` + +`informed-decision` intake `INFD-IN-0005` (open, `origin_ref: SECRETS-WP-0010`): +platform-root's approval of `infd-20260927-b01` was refused by flex-auth's +`compact_sitting` policy with `policy_denied`, despite platform-root having just +completed a fresh key-cape MFA sign-in. flex-auth's 900-second review-freshness +check reads `assurance.at`, which key-cape derives from the PKCE session's +`AuthTime`. + +Root cause in `src/internal/server/oidc/authorize.go`, +`AuthorizeHandler.completeAuthorization`: whenever a `kc_login` cookie already +existed for the same username, its `IssuedAt` was unconditionally reused as +`authTime` — even on the two call sites that had just called +`MFA.ValidateMFAToken` successfully in this exact request (the callback's +`mfaToken != ""` branch and `serveMFASubmission`). A user who had an older +browser session and then stepped up with a fresh MFA challenge got the *old* +session's timestamp stamped onto the *new*, just-proven authentication — +understating freshness for every downstream binding-grade check, exactly as +`INFD-IN-0005` describes ("completeAuthorization currently copies a prior +timestamp... only after actual reauthentication"). + +The reuse itself is correct when MFA was **not** just performed in this +request (`decision.MFAVerified` carried forward from an existing AAL2 session, +via `domain.DecideAssurance`) — reporting mint time there would overstate +freshness (`TestAssuranceReportsAuthenticationTimeNotMintTime`). The two cases +were conflated under a single `mfaVerified` bool. + +Fix: added a `freshMFA bool` parameter to `completeAuthorization`, `true` only +at the two call sites that just validated an MFA token in this request, `false` +at the carried-forward-assurance call site. `authTime` now falls back to the +prior session's `IssuedAt` only when `!freshMFA`. + +Added `TestAuthorizeCallback_MFASubmission_FreshEvenWithStalePriorLoginCookie`, +which seeds a 4-hour-old `kc_login` cookie for the same user, then submits a +valid MFA token and asserts the resulting `AuthTime` is within a minute of now. +Confirmed it fails against the pre-fix code (`AuthTime` ~4h old) and passes +after. Full suite (`go test ./...`) green, no other test needed adjustment — +the carried-forward-assurance case (`TestPolicy_...`, `TestLogout_...`, +`assurance_time_test.go`) is unaffected since none of it exercises a fresh MFA +submission with a pre-existing cookie for the same user. + +Does not touch flex-auth (its 900s window is correct; it was consuming a bad +timestamp) or informed-decision (correctly refuses to interpret the PDP's +denial). `INFD-IN-0005` can close once informed-decision confirms platform-root +can approve after a fresh sign-in against this build. + +## Discovery/JWKS fetch was missing the split-horizon origin headers + +```task +id: ADHOC-2026-09-27-T02 +status: done +priority: high +``` + +Rolling out T01 (image digest `sha256:248e449a...`) broke every KeyCape login, +including unrelated clients (`user-engine-portal`), with `auth_failure` / +`provider_metadata_unavailable` at `/api/oidc/token` on the KeyCape side and +Authelia logging `invalid X-Forwarded-Proto header value 'http'` on +`GET /.well-known/openid-configuration` from KeyCape's pod IP. + +Root cause, unrelated to T01's change: `AutheliaAdapter.setPublicOriginHeaders` +(the NK-WP-0041-T02/path-B fix, key-cape@3b0446e) annotates only the token +exchange (`newTokenRequest`) with `X-Forwarded-Proto`/`X-Forwarded-Host` so +Authelia can derive the public issuer for a split-horizon (in-cluster HTTP, +public HTTPS) deployment. `idTokenVerifier.get` in `verify.go` — used for the +provider-metadata and JWKS fetches that verify every upstream ID token — never +got the same treatment; it built a bare request with only an `Accept` header. +Authelia 4.38 fails closed on the resulting unmarked in-cluster request. This +gap predates today's session (present since 3b0446e); it surfaced now because +this was the first login attempted since that image went live. + +Fix: extracted `setPublicOriginHeaders` from an `AutheliaAdapter` method to a +package-level function taking the target public base URL explicitly, and gave +`idTokenVerifier` its own resolved `publicBaseURL` (same +`BrowserBaseURL`-else-`BaseURL` fallback as the adapter) so `get()` applies +the same headers. Added `TestDiscoveryFetch_CarriesPublicOriginHeaders` +(confirmed it fails without the fix, passes with it). Full suite green. + +New image `key-cape@` still needs a rollout to `sso-mfa/k8s/keycape/deployment.yaml` +before platform-root can sign in again. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0001-keycape-implementation.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0001-keycape-implementation.md new file mode 100644 index 00000000..74334411 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0001-keycape-implementation.md @@ -0,0 +1,499 @@ +--- +id: KEY-WP-0001 +type: workplan +title: "KeyCape Implementation — Lightweight IAM Profile" +domain: infotech +repo: key-cape +status: finished +owner: Bernd +topic_slug: netkingdom +topic_id: a6c6e745-bf54-4465-9340-1534a2be493e +repo_id: 8a99bb74-1ec0-4478-ac70-35a7cddb0e3c +created: 2026-03-13 +updated: 2026-03-13 +spec_refs: + - wiki/KeyCapeSpecification_v0.1.md + - wiki/KeyCapeSpecificationPack_v0.1.md +decisions: + - id: ADR-0001 + title: "Implementation language: Go" + hub_decision_id: 620beb04-fa3f-4a9d-9806-02890a7a2b0d + status: accepted + ref: docs/adr/ADR-0001-choose-go-for-keycape.md +state_hub_workstream_id: "0d34dfc1-7ccb-4bd5-b872-5c7379b9adce" +--- + +# KEY-WP-0001 — KeyCape Implementation + +Implements the **NetKingdom IAM Profile** via KeyCape: a stateless, profile-constrained OIDC +server orchestrating Authelia, LLDAP, and privacyIDEA. Replaceable by Keycloak without +application changes. + +## Language Decision + +**Go** — decided 2026-03-13 by Bernd. See `docs/adr/ADR-0001-choose-go-for-keycape.md`. + +KeyCape is an orchestrating boundary service (HTTP, adapters, JWT via library, CLI tooling) — +Go's strongest domain. Rust revisitable if a subcomponent needs stronger guarantees later. +Mandatory guardrails: typed domain models, narrow adapter interfaces, layered architecture, +fuzz tests on validator/redirect/claim-mapping, no cleverness. + +## Repo Structure (from spec §12) + +``` +src/ + server/ + oidc/ # Profile endpoints + telemetry/ # Structured event emission + errors/ # Error taxonomy + enforcement middleware + adapters/ + authelia/ # Auth flow delegation + lldap/ # Identity directory reads + privacyidea/ # MFA enforcement + validator/ # Canonical LDAP schema validator binary + migration/ + lldap-export/ # LLDAP → canonical + keycape-to-keycloak/ # Canonical → Keycloak realm import + lldap-to-ldap/ # LLDAP → OpenLDAP/389DS/AD LDIF +spec/ + canonical-model.yaml # Source of truth for all identity data + ldap-schema.yaml # Canonical LDAP schema rules +tests/ + profile/ # Scenario A — lightweight baseline + negative/ # Scenario D — unsupported feature rejection + migration/ # Scenarios B & C — replacement +``` + +## Dependency Order + +``` +T01 (project setup) + └─ T02 (canonical model) T04 (error taxonomy) + └─ T03 (LDAP validator) └─ T13 (telemetry) + └─ T10 (LLDAP adapter) └─ T14 (enforcement layer) + └─ T11 (Authelia) │ + └─ T12 (privacyIDEA) │ + │ │ + T05 ─ T06 ─ T07 ─ T08 ─┴─ T09 (OIDC server) + │ + T18 (profile tests / Scenario A) + T21 (negative tests / Scenario D) + │ + T15 → T16 → T19 (Scenario B) + T15 → T17 → T20 (Scenario C) + │ + T22 (dev stack) + T23 (production packaging) +``` + +--- + +## Phase 1 — Foundations + +## T01 — Project setup: Go module, repo layout, CI skeleton + +```task +id: KEY-WP-0001-T01 +hub_task_id: 25613e3f-2a65-409e-afaa-d23ded0bc256 +priority: high +status: done +state_hub_task_id: "38822bc0-4189-4909-874e-ea40e5771250" +``` + +Initialise language module in `src/`. Create directory skeleton per spec §12. Add Makefile +targets: `build`, `test`, `lint`. Set up CI (build + test on every push). Scaffolding only — +no application code. **Agent must call `record_decision()` with chosen language (Go or Rust).** + +## T02 — Canonical identity model: machine-readable schema + +```task +id: KEY-WP-0001-T02 +hub_task_id: deee2929-9386-41db-bf91-fbd9ad646c28 +priority: high +status: done +depends_on: [T01] +state_hub_task_id: "940c118b-c1e6-4dda-bd4c-4fac105822be" +``` + +Write `spec/canonical-model.yaml`. Six entities: User, Group, Role, Client, Membership, +MFAEnrollment (fields per spec §2). Include JSON Schema or CUE schema for programmatic +validation. This file is the **source of truth** — all other code derives from it. + +## T03 — Canonical LDAP schema + validator + +```task +id: KEY-WP-0001-T03 +hub_task_id: 02592c65-db23-474b-b06b-019e95df8146 +priority: high +status: done +depends_on: [T01, T02] +state_hub_task_id: "c1715d70-f10f-45e9-b73a-b54a3d360342" +``` + +Write `spec/ldap-schema.yaml`: tree layout (`ou=users`, `ou=groups`, `ou=clients` under +`dc=netkingdom,dc=local`), object classes (`inetOrgPerson`, `groupOfNames`), required/optional +attributes. Implement `validator/` binary. Structural rules: valid DN, required attrs, no unknown +attrs, valid group memberships. Semantic rules: referenced users exist, no cycles, usernames +unique, email format valid. Validator runs in `--mode=ci|provisioning|migration`. Emits +machine-readable report. + +## T04 — Error taxonomy: types, JSON format, middleware + +```task +id: KEY-WP-0001-T04 +hub_task_id: 46870fd6-0672-432b-8824-6bc2e24811b3 +priority: high +status: done +depends_on: [T01] +state_hub_task_id: "6e3b6b97-ac77-44c5-959e-be12751f1b63" +``` + +Implement four error types (spec §5): +- `feature_not_supported_by_profile` +- `available_in_keycloak_mode_only` +- `rejected_for_profile_safety` +- `invalid_profile_usage` + +JSON format: `{"error": "...", "description": "...", "feature": "..."}`. HTTP middleware wraps +all handler errors. Error type strings are stable and test-assertable. + +--- + +## Phase 2 — OIDC Server + +## T05 — OIDC discovery endpoint (/.well-known/openid-configuration) + +```task +id: KEY-WP-0001-T05 +hub_task_id: 92eb8916-cb22-4786-9f16-a8a07272f818 +priority: high +status: done +depends_on: [T04] +state_hub_task_id: "0dbc08e3-c465-4c37-a219-832a580bedfd" +``` + +`GET /.well-known/openid-configuration`. Advertise **only** profile-supported features: +`authorization_code`, S256 PKCE, RS256, static scopes. Must NOT advertise: dynamic registration, +implicit flow. Issuer configurable. Cacheable response. + +## T06 — Authorization endpoint with PKCE and redirect URI validation + +```task +id: KEY-WP-0001-T06 +hub_task_id: c3df620b-9864-4ff1-ba6d-26057c6f4d59 +priority: high +status: done +depends_on: [T04, T11, T12, T13, T14] +state_hub_task_id: "cdb4b06d-3d54-49dd-ac05-ca9ed6d7322f" +``` + +`GET/POST /authorize`. Validate: `client_id` (static config), `redirect_uri` (exact match — +wildcard → `rejected_for_profile_safety`), `response_type=code`, `scope` contains `openid`, +`code_challenge` present (missing → `invalid_profile_usage`), `code_challenge_method=S256`. +Delegate to Authelia adapter. Store PKCE state server-side. No implicit or hybrid flow. + +## T07 — Token endpoint: JWT/RS256, canonical claim mapping + +```task +id: KEY-WP-0001-T07 +hub_task_id: d3248f4d-e0e9-4144-9844-c9768dc896d6 +priority: high +status: done +depends_on: [T06, T08, T10] +state_hub_task_id: "534d8616-90de-4d32-961c-c2ef719642e4" +``` + +`POST /token`. Validate PKCE `code_verifier`. Issue RS256 JWT via standard library (no custom +crypto). Mandatory claims: `iss`, `sub` (canonical user ID), `aud`, `exp`, `iat`. Optional +claims: `preferred_username` (LDAP `uid`), `email` (LDAP `mail`), `groups` (groupOfNames), +`roles`. Short, explicitly configured token lifetime. Any other grant type → +`feature_not_supported_by_profile`. + +## T08 — JWKS endpoint (/jwks) + +```task +id: KEY-WP-0001-T08 +hub_task_id: 58a1d705-b788-4d66-8c0a-33edff63a885 +priority: high +status: done +depends_on: [T01] +state_hub_task_id: "7e2167be-bcc7-49c2-8681-e518abd5bc0c" +``` + +`GET /jwks`. RS256 public key in JWK Set format. Key loaded from config. Key rotation: serve +multiple keys during rotation window, keyed by `kid`. Standard library key generation only. + +## T09 — Userinfo endpoint (/userinfo) + +```task +id: KEY-WP-0001-T09 +hub_task_id: 742d3924-21e9-4304-86e9-0400af0e81ee +priority: medium +status: done +depends_on: [T07, T10] +state_hub_task_id: "78094ca5-a831-4443-9ccf-fc476ff87b91" +``` + +`GET /userinfo`. Optional per spec — implement if any registered client requires it. Validate +Bearer token (RS256 + expiry). Return claim subset scoped to granted scopes. Claims must be +identical to ID token for same scopes. If no client needs it: stub returning +`available_in_keycloak_mode_only`. + +--- + +## Phase 3 — Backend Adapters + +## T10 — LLDAP adapter: user and group reads + +```task +id: KEY-WP-0001-T10 +hub_task_id: 2043b10a-6822-45f8-abcc-4e233d918fb0 +priority: high +status: done +depends_on: [T02, T03] +state_hub_task_id: "97d19662-f482-4ea5-84fd-9fccb84ff317" +``` + +`adapters/lldap`. LDAP protocol connection to LLDAP. Interface: `LookupUser(username) → canonical +User`, `LookupGroups(userDN) → []Group`, `ValidatePassword(username, password) → bool`. Attribute +map: `uid→username`, `cn→displayName`, `mail→email`, `memberOf→groups`. Run canonical LDAP schema +validator on every read. No LDAP internals exposed to `server/`. + +## T11 — Authelia adapter: session and auth flow delegation + +```task +id: KEY-WP-0001-T11 +hub_task_id: ad129a14-1552-4717-b1dd-b529d18ce681 +priority: high +status: done +depends_on: [T04, T13] +state_hub_task_id: "6461865b-f57c-4591-9cf3-68c79af22723" +``` + +`adapters/authelia`. Initiate auth redirect to Authelia, receive callback, extract authenticated +user identity, hand off to MFA check (T12). Must not leak Authelia session tokens/cookies into +profile layer. Unavailable Authelia → fail closed (`auth_failure` event). + +## T12 — privacyIDEA adapter: MFA enforcement + +```task +id: KEY-WP-0001-T12 +hub_task_id: 1ef196e6-2304-4cf6-b205-47ac1da879ec +priority: high +status: done +depends_on: [T02, T13] +state_hub_task_id: "e403a783-c856-4d6d-b859-a9cad7545fe1" +``` + +`adapters/privacyidea`. **KeyCape must NOT implement MFA logic.** Interface: +`CheckMFARequired(user) → bool`, `ValidateMFAToken(user, token) → bool`. MFA failure → no token +issued + `auth_failure` telemetry. MFA enrollment state from canonical `MFAEnrollment` entity. +privacyIDEA remains stable across lightweight → expanded migration. + +--- + +## Phase 4 — Telemetry & Enforcement + +## T13 — Telemetry pipeline: structured event emission + +```task +id: KEY-WP-0001-T13 +hub_task_id: 704146bf-cd60-4922-b18b-3d209cff3ac3 +priority: high +status: done +depends_on: [T01] +state_hub_task_id: "4df7bda1-5b84-4b4c-9b16-bcb1d3cca096" +``` + +`server/telemetry`. Event types (spec §6.1): `auth_start`, `auth_success`, `auth_failure`, +`token_issued`, `unsupported_feature`, `invalid_request`, `migration_event`. Required fields +(spec §6.2): `timestamp`, `client_id`, `endpoint`, `feature`, `result`, `error_type`, `scopes`, +`grant_type`, `environment`, `trace_id`. Pluggable outputs: structured log (default), Prometheus +metrics endpoint. Every auth and error path emits an event — **no silent paths**. + +## T14 — Unsupported feature enforcement layer + +```task +id: KEY-WP-0001-T14 +hub_task_id: 71f44886-ab61-4160-a435-72b35af472a0 +priority: high +status: done +depends_on: [T04, T13] +state_hub_task_id: "ae16fba9-5bb4-4780-ac77-558e3ed7e1dd" +``` + +`server/errors` enforcement middleware. Intercept any parameter, grant type, scope, or client +config exceeding the profile. Return correct error type + emit `unsupported_feature` telemetry. +Maintain a **registry** of unsupported features (adding new ones requires no handler changes): +`dynamic_client_registration`, `identity_broker`, `wildcard_redirect_uri`, `implicit_flow`, etc. +Every registry entry must have a corresponding test in T21. + +--- + +## Phase 5 — Migration Tooling + +## T15 — Migration: LLDAP → canonical export + +```task +id: KEY-WP-0001-T15 +hub_task_id: 1bd13f76-2d62-429d-b230-d785ef6a3f2f +priority: medium +status: done +depends_on: [T02, T03, T10] +state_hub_task_id: "f7549cd7-33f0-4407-a656-ab8f5a184e64" +``` + +`migration/lldap-export` tool. Read all users, groups, memberships, attributes from LLDAP. Map +to canonical model. Validate against LDAP schema validator before writing. Output: +`canonical-export.yaml`. Emit `migration_event` telemetry. Idempotent. Include incompatibility +report for unmappable LLDAP data. + +## T16 — Migration: canonical → Keycloak import + +```task +id: KEY-WP-0001-T16 +hub_task_id: f3d50e80-f6b5-4c0c-a5f3-08308ea1a95e +priority: medium +status: done +depends_on: [T15] +state_hub_task_id: "96486c41-9f33-42a5-b7b6-ad0a9eb2bdee" +``` + +`migration/keycape-to-keycloak` tool. Read canonical export (T15). Transform to Keycloak realm +import format: users, groups, clients, roles, scope mappings. Preserve: same issuer, same claims, +same scopes, same client behavior. Output: `keycloak-realm-import.json`. Emit `migration_event`. +Include round-trip validation report. + +## T17 — Migration: LLDAP → full LDAP (OpenLDAP / 389DS / AD) + +```task +id: KEY-WP-0001-T17 +hub_task_id: 044d99c8-39cb-4f35-9308-912ae829bd22 +priority: medium +status: done +depends_on: [T15] +state_hub_task_id: "1ec335a2-80ca-4c34-b08e-211f537e4214" +``` + +`migration/lldap-to-ldap` tool. Export via T15 canonical export. Generate LDIF for target +(`--target=openldap|389ds|ad`). Migrate: users, groups, memberships, attributes. Run validator +on output LDIF before import. Produce validation report. **Orthogonal to T16** — the two +migration dimensions are independent (spec §14.1). + +--- + +## Phase 6 — Replacement Tests + +## T18 — Profile test suite: Scenario A (lightweight baseline) + +```task +id: KEY-WP-0001-T18 +hub_task_id: 76abc3f6-9c4e-4aca-9995-72b728925812 +priority: high +status: done +depends_on: [T05, T06, T07, T08, T09, T22] +state_hub_task_id: "1b0e9f26-d441-42b8-b532-1eb713fb355d" +``` + +`tests/profile`. Provision canonical fixtures into LLDAP + Authelia + KeyCape. Test categories +(spec §15.3): discovery, login flow (PKCE), token claim assertions (all mandatory + optional), +redirect validation, client config, MFA policy, logout (if implemented). Tests are +**backend-agnostic** — same suite runs in T19 and T20. Must pass for Scenario A conformance. + +## T19 — Replacement test suite: Scenario B (IAM swap, same directory) + +```task +id: KEY-WP-0001-T19 +hub_task_id: 56d03e89-934b-4992-bfe4-b32f275882e3 +priority: medium +status: done +depends_on: [T18, T16] +state_hub_task_id: "a02d24e7-32de-4be6-935c-896c10dde020" +``` + +Run T18 suite against Keycloak + LLDAP (configured from T16 canonical export). **No test code +changes allowed.** Migration successful only if all T18 tests pass. Proves IAM replaceability +without directory migration. + +## T20 — Replacement test suite: Scenario C (full expansion) + +```task +id: KEY-WP-0001-T20 +hub_task_id: ec3cae5c-9942-4be7-acc0-1eb9f02aba45 +priority: medium +status: done +depends_on: [T19, T17] +state_hub_task_id: "545f319f-053d-48bd-8d94-c8c05cd56736" +``` + +Apply T17 LLDAP→OpenLDAP migration, then T16 Keycloak import. Run T18 suite. Migration successful +only if all tests pass. privacyIDEA must remain stable (no MFA re-enrollment required). + +## T21 — Negative profile tests: Scenario D (unsupported feature rejection) + +```task +id: KEY-WP-0001-T21 +hub_task_id: a5112b63-121f-4d17-ac1a-fb46d160413e +priority: high +status: done +depends_on: [T14] +state_hub_task_id: "5856afe0-2a9e-4489-b057-35e59f86c359" +``` + +`tests/negative`. For every entry in T14 unsupported-feature registry: attempt usage, assert +correct `error.error` string, assert `unsupported_feature` telemetry event emitted. Covered +cases: `dynamic_client_registration`, `implicit_flow`, wildcard redirect URIs, identity brokering, +missing PKCE, unknown scopes, unknown grant types. **Must run in CI.** Proves enforcement layer +is complete. + +--- + +## Phase 7 — Operations + +## T22 — Dev mode stack: docker-compose with LLDAP + Authelia + +```task +id: KEY-WP-0001-T22 +hub_task_id: e840963f-cd19-4b38-857a-7c40df165d3d +priority: medium +status: done +depends_on: [T01] +state_hub_task_id: "b98f2671-a20a-4438-99c9-fbe0e5324534" +``` + +`docker-compose.dev.yml`: KeyCape, LLDAP, Authelia, privacyIDEA (or stub). Pre-seeded with +canonical fixtures from T02. Makefile targets: `make dev` (start + verify basic auth flow), +`make seed` (re-apply fixtures without full restart). Deterministic: same seed → same state. +Test environment for T18 and T21. + +## T23 — Slim production packaging: binary + config + health + +```task +id: KEY-WP-0001-T23 +hub_task_id: d5723683-f739-4362-b62e-71213dc5a89e +priority: low +status: done +depends_on: [T18, T21] +state_hub_task_id: "8c1752c2-7fb3-4da5-aab3-6b7acf12ea64" +``` + +Single stateless binary. Declarative YAML config: profile version, client definitions, backend +connections, LDAP schema rules, privacyIDEA settings, telemetry destination, token lifetime, +issuer URL. Static config validation on startup. `/healthz` endpoint. Minimal container image +(distroless or Alpine). Config environment-promotable via env var overrides only. + +--- + +## Acceptance Criteria (from spec §15.4 and §20) + +A release is conformant when: + +- [x] Scenario A tests pass (T18) — `src/tests/profile/profile_test.go` (8 tests) +- [x] Scenario D tests pass (T21) — `src/tests/negative/negative_test.go` (8 tests) +- [x] Scenario B tests pass (T19) — `src/tests/migration/scenario_b_test.go` (7 tests) +- [x] Scenario C tests pass (T20) — `src/tests/migration/scenario_c_test.go` (6 tests) +- [x] All error responses use taxonomy types from spec §5 — `internal/errors/taxonomy.go` +- [x] All auth/error paths emit structured telemetry (T13) — `internal/server/telemetry/` +- [x] Canonical LDAP schema validator passes on all fixtures (T03) — `internal/validator/` +- [x] No handwritten cryptography anywhere in the codebase — stdlib `crypto/rsa` only +- [x] Config is statically validated at startup (T23) — `internal/config/validate.go` diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0002-container-image-gitea.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0002-container-image-gitea.md new file mode 100644 index 00000000..418f477c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0002-container-image-gitea.md @@ -0,0 +1,245 @@ +--- +id: KEY-WP-0002 +type: workplan +title: "KeyCape Container Image — Build & Publish to Gitea OCI Registry" +domain: infotech +repo: key-cape +status: finished +owner: netkingdom +topic_slug: netkingdom +created: "2026-03-22" +updated: "2026-03-21" +capability_request_id: "" +state_hub_workstream_id: "c8843c7a-460a-47a2-b45a-b8d3940f9aa2" +--- + +# KEY-WP-0002 — KeyCape Container Image — Build & Publish to Gitea OCI Registry + +## Problem + +KeyCape has a `Dockerfile` but no automated build pipeline and no published +image. Other services (k3s deployments, local dev) that need to run KeyCape +must build locally from source. There is no versioned artefact to reference +in Helm charts or manifests. + +The capability request for this work was originally misrouted to railiance. +It belongs here: KeyCape owns its own image. + +## Goal + +Produce a versioned OCI image for KeyCape, published to the Gitea container +registry on CoulombCore (`92.205.130.254:32166`), triggered automatically on +every merge to `main` and on semver tags (`v*`). + +**Gitea OCI registry endpoint:** `92.205.130.254:32166` +**Image name:** `92.205.130.254:32166/coulomb/key-cape` + +> **Why Gitea, not GHCR?** +> The net-kingdom cluster is self-hosted. Keeping images in Gitea (also +> self-hosted on CoulombCore) avoids any external registry dependency and +> keeps image pulls within the cluster network. GHCR is a future option +> once public distribution is needed. + +## Design + +### Image naming & tagging + +| Trigger | Tags applied | +|---------|-------------| +| push to `main` | `latest`, `main-` | +| tag `v1.2.3` | `1.2.3`, `1.2`, `1`, `latest` | + +### Build + +Multi-stage Dockerfile already present — no changes needed to the build +itself. The image builds to a distroless static binary (~10 MB). + +### Registry auth + +Gitea issues a personal access token (or machine account token) with +`write:packages` scope. Stored as Gitea Actions secret `REGISTRY_TOKEN`; +username stored as `REGISTRY_USER`. + +For local `make push`, credentials are passed via `docker login` before +the push target runs. + +### Makefile targets + +```makefile +IMAGE_REGISTRY ?= 92.205.130.254:32166 +IMAGE_REPO ?= coulomb/key-cape +IMAGE_TAG ?= latest +IMAGE := $(IMAGE_REGISTRY)/$(IMAGE_REPO):$(IMAGE_TAG) + +image: + docker build -t $(IMAGE) . + +push: image + docker push $(IMAGE) + +image-tag: + docker tag $(IMAGE) $(IMAGE_REGISTRY)/$(IMAGE_REPO):$(IMAGE_TAG) +``` + +### Gitea Actions workflow + +`.gitea/workflows/image.yaml` — triggers on push to `main` and on `v*` tags: +- Checkout +- Set up Docker Buildx +- Login to `92.205.130.254:32166` using secrets +- Build and push with metadata-action tags +- (Optional) sign with cosign if available + +### k3s insecure registry + +Gitea runs over plain HTTP on port 32166 (NodePort). k3s must be configured +to treat this endpoint as an insecure registry so image pulls work from +within the cluster: + +```yaml +# /etc/rancher/k3s/registries.yaml (on CoulombCore) +mirrors: + "92.205.130.254:32166": + endpoint: + - "http://92.205.130.254:32166" +``` + +k3s picks this up on restart (or SIGHUP). Worker nodes (if any) need the +same file. + +--- + +## Tasks + +### T01 — Makefile: image, push, image-tag targets + +```task +id: KEY-WP-0002-T01 +status: done +priority: high +state_hub_task_id: "749472fc-edb9-4948-9ebc-58d5f38327ee" +``` + +Add `image`, `push`, and `image-tag` targets to `Makefile` with +`IMAGE_REGISTRY`, `IMAGE_REPO`, `IMAGE_TAG` variables defaulting to the +Gitea endpoint and `coulomb/key-cape:latest`. + +Gate: `make image` builds successfully locally; `IMAGE_TAG=dev make image` +produces a differently-tagged image. + +--- + +### T02 — Gitea Actions workflow + +```task +id: KEY-WP-0002-T02 +status: done +priority: high +state_hub_task_id: "8ecf18cc-a3bb-4ede-a09c-fcd0d26d7f9d" +``` + +Create `.gitea/workflows/image.yaml`: +- Trigger: `push` to `main`, `push` tags matching `v*` +- Runner: `act_runner` label (or `ubuntu-latest` if configured) +- Steps: checkout → docker buildx → login → build+push +- Tags via `docker/metadata-action`: `latest` on main, semver on tags + +Secrets required (document in README.md under "CI"): +- `REGISTRY_USER` — Gitea username or machine account +- `REGISTRY_TOKEN` — Gitea personal access token with `write:packages` + +Gate: workflow file is syntactically valid; documented in README. + +--- + +### T03 — k3s insecure registry config on CoulombCore + +```task +id: KEY-WP-0002-T03 +status: done +priority: high +state_hub_task_id: "2dde67f9-944f-418d-a2e9-7367bc556425" +``` + +On CoulombCore, create/update `/etc/rancher/k3s/registries.yaml` to add +the Gitea NodePort as an HTTP mirror. Restart k3s (or send SIGHUP) and +verify `crictl pull 92.205.130.254:32166/coulomb/key-cape:latest` works. + +Gate: image pull from within the cluster succeeds without TLS errors. + +--- + +### T04 — Create Gitea machine account & token + +```task +id: KEY-WP-0002-T04 +status: done +priority: medium +state_hub_task_id: "25775e10-3164-4adb-9c41-835c86fde5f8" +``` + +In Gitea (http://92.205.130.254:32166), create a machine account +`ci-netkingdom` (or reuse an existing service account) with access to +the `netkingdom` organisation. Generate a token with `write:packages` +scope and store it in: +- Gitea Actions secrets on the `key-cape` repo: `REGISTRY_USER`, `REGISTRY_TOKEN` +- The net-kingdom credential store (SOPS-encrypted) under + `credentials/gitea-ci-token.enc.yaml` + +Gate: `docker login 92.205.130.254:32166` succeeds with the token; +secret is in the credential store. + +--- + +### T05 — Smoke test: push and pull a dev image + +```task +id: KEY-WP-0002-T05 +status: done +priority: medium +state_hub_task_id: "0f6ab38f-6d34-41af-9180-f19c687947b5" +``` + +Manually trigger a build-and-push: +```bash +docker login 92.205.130.254:32166 +IMAGE_TAG=dev make push +``` + +Then verify the image is pullable from CoulombCore: +```bash +# on CoulombCore +crictl pull 92.205.130.254:32166/coulomb/key-cape:dev +``` + +Gate: pull succeeds; image is listed in Gitea -> Packages -> coulomb/key-cape. + +--- + +### T06 — Update README with registry & CI docs + +```task +id: KEY-WP-0002-T06 +status: done +priority: low +state_hub_task_id: "946cd34d-94da-4fa9-a781-ed36f6c827a3" +``` + +Add a "Container Image" section to `README.md` documenting: +- Registry URL and image name +- How to pull (`docker pull 92.205.130.254:32166/coulomb/key-cape:latest`) +- How to build and push locally (Makefile targets) +- CI secrets required for the Actions workflow + +Gate: README section present and accurate. + +--- + +## Done Criteria + +- [ ] `make image` and `make push` work locally +- [ ] `.gitea/workflows/image.yaml` present and documented +- [ ] k3s can pull the image from Gitea without TLS errors +- [ ] Machine account token stored in credential store +- [ ] Smoke test: `dev` image pushed and pulled successfully +- [ ] README updated diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md new file mode 100644 index 00000000..9896c67d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md @@ -0,0 +1,200 @@ +--- +id: KEY-WP-0003 +type: workplan +title: "Bootstrap Console OIDC Login and MFA Verification" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-05-24" +updated: "2026-05-24" +state_hub_workstream_id: "02990009-a2b3-44f6-a579-487fbacae41a" +--- + +# KEY-WP-0003 - Bootstrap Console OIDC Login and MFA Verification + +## Problem + +The NetKingdom security bootstrap console now acts as a local OIDC client +callback so the operator can verify the dedicated platform-root login before +approving custody mode. The current live KeyCape deployment rejects that flow +with: + +```json +{ + "error": "invalid_profile_usage", + "description": "redirect_uri does not match any registered URI", + "feature": "redirect_uri" +} +``` + +That error is correct profile enforcement: KeyCape only accepts exact +registered redirect URIs. The live `demo-app` registration has not yet been +updated to allow the local bootstrap console callback: + +- `http://127.0.0.1:8876/oidc/callback` +- `http://localhost:8876/oidc/callback` + +After that is fixed, there is a second usability/security gap. KeyCape checks +privacyIDEA MFA after the Authelia callback, but the browser flow currently +expects an `mfa_token` query parameter instead of presenting a proper OTP +challenge page to the human operator. + +## Goal + +Make the bootstrap console's "Start demo OIDC login" button a real +end-to-end verification path for the current lightweight IAM stack: + +1. KeyCape accepts the bootstrap console callback URI by exact registration. +2. The browser leaves KeyCape for the public Authelia login URL. +3. After password login, KeyCape presents a minimal MFA challenge when + privacyIDEA requires one. +4. KeyCape issues an OIDC authorization code to the bootstrap console callback. +5. The console can exchange the code and let the operator mark + `OIDC login verified` without exposing tokens or secrets. + +This keeps KeyCape's security posture intact: no wildcard redirect URIs, no +dynamic client registration, no token display, and no storage of OTP material. + +## Design Notes + +- Prefer a dedicated public client named `netkingdom-bootstrap-console` for + long-lived clarity. Reusing `demo-app` is acceptable for the immediate + unblock only if the deployment/runbook clearly labels it as a bootstrap test + client. +- The bootstrap callback is local-only and operator-attended. It must be an + exact URI in config, not a wildcard or dynamic registration exception. +- Browser-facing Authelia redirects must use the public Authelia base URL + (`https://auth.coulomb.social`) so the human login page opens correctly. +- KeyCape may still need an internal service URL for back-channel token + exchange. If so, split the current single Authelia URL into browser-facing + authorize URL and internal token URL instead of making the browser use an + in-cluster hostname. +- The MFA prompt should collect only a one-time code, post it back to KeyCape, + validate with privacyIDEA, and then continue the normal OIDC code flow. +- This work unblocks the NetKingdom custody gate in + `NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap`. + +## Implementation Notes + +**2026-05-24:** Implemented in source: + +- added `netkingdom-bootstrap-console` as a public OIDC client in the sample + KeyCape config, while keeping the local callback registered on `demo-app` + for compatibility, +- split Authelia browser redirects from server-side token exchange via + `browserBaseURL` and `tokenBaseURL`, +- added a browser MFA challenge page at `POST /authorize/callback` that + validates the one-time code with privacyIDEA before issuing the downstream + OIDC authorization code, +- updated NetKingdom's `keycape-config` generation template and bootstrap + console to use the dedicated client, +- added regression tests for callback registration, split Authelia URLs, MFA + challenge rendering, valid OTP continuation, and invalid OTP failure. + +Live use still requires deployment: build/publish the updated KeyCape image, +refresh the live `keycape-config` Secret through the custodian age-key unlock +ceremony, and restart the KeyCape deployment. + +--- + +## T01 - Register the bootstrap console callback client + +```task +id: KEY-WP-0003-T01 +status: done +priority: high +state_hub_task_id: "b396c99f-d711-475a-9cba-4f03a1db561d" +``` + +Add a KeyCape client registration for the bootstrap console. Either create a +dedicated `netkingdom-bootstrap-console` public client or update `demo-app` +temporarily with these exact redirect URIs: + +- `http://127.0.0.1:8876/oidc/callback` +- `http://localhost:8876/oidc/callback` + +Update the sample config, tests, and deployment/runbook references so the +registered client is reproducible and not just a live-cluster patch. + +Gate: an authorize request using the local callback no longer returns +`invalid_profile_usage` for `redirect_uri`. + +## T02 - Separate browser-facing and internal Authelia URLs if needed + +```task +id: KEY-WP-0003-T02 +status: done +priority: high +state_hub_task_id: "46172e6d-3e11-493c-b223-79c2fc321aec" +``` + +Confirm whether the current `authelia.baseURL` is safe to use for both browser +redirects and server-side token exchange. If not, add explicit configuration +for the browser authorize base URL and internal token/userinfo base URL. + +Gate: the first browser redirect leaves `https://kc.coulomb.social` for +`https://auth.coulomb.social/...`; server-side token exchange still works from +inside the deployment. + +## T03 - Add a browser MFA challenge step + +```task +id: KEY-WP-0003-T03 +status: done +priority: high +state_hub_task_id: "92fca4d0-6215-4ea6-9f80-9178ae183acb" +``` + +When `CheckMFARequired` returns true after the Authelia callback, render a +minimal KeyCape MFA challenge page instead of requiring `mfa_token` in the +callback query string. The page should: + +- show the authenticated username and client display name, +- collect only the OTP code, +- preserve the pending OIDC state server-side, +- validate with privacyIDEA, +- continue to issue the normal authorization code on success, +- fail closed with the existing telemetry on invalid MFA. + +Gate: a user enrolled in privacyIDEA can complete password + OTP in the +browser and is returned to the registered downstream callback. + +## T04 - Add end-to-end profile tests for the bootstrap login path + +```task +id: KEY-WP-0003-T04 +status: done +priority: medium +state_hub_task_id: "079a5929-1864-4461-a64c-746cebca469d" +``` + +Add tests that cover: + +- local bootstrap callback registration, +- rejection of unregistered callbacks remains intact, +- Authelia browser redirect uses the expected public URL, +- MFA-required login presents a challenge instead of immediate failure, +- invalid OTP fails closed, +- valid OTP produces an authorization code bound to the original PKCE session. + +Gate: `make test` passes and the negative redirect URI tests remain green. + +## T05 - Document the live rollout ceremony + +```task +id: KEY-WP-0003-T05 +status: done +priority: medium +state_hub_task_id: "1d67225d-a20b-4e36-9b2e-20836be2f439" +``` + +Document the deployment path for updating live KeyCape config without +regenerating unrelated secrets. The runbook must fit the NetKingdom custodian +age-key model: decrypt or unlock only during an attended ceremony, apply the +updated client registration/config, restart KeyCape, and remove plaintext +secret material afterward. + +Gate: an operator can update the live `keycape-config` Secret and verify the +bootstrap console OIDC login without printing or committing secrets. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md new file mode 100644 index 00000000..a50e9105 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md @@ -0,0 +1,332 @@ +--- +id: KEY-WP-0004 +type: workplan +title: "Binky Hedgehog GmbH as first NetKingdom tenant + qonto runtime workload identity" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-07-23" +updated: "2026-07-29" +state_hub_workstream_id: "817709be-5756-5ef7-9ccd-dcb84b6b9c64" +--- + +# Binky Hedgehog GmbH as first NetKingdom tenant + qonto runtime workload identity + +Binky Hedgehog GmbH ("binky") becomes the **first real tenant** provisioned in +the NetKingdom IAM stack, per the NetKingdom IAM Profile v0.2's tenant claim +model (`net-kingdom/canon/standards/iam-profile_v0.2.md` §"Tenant Claim") — +distinct from `tenant:platform` (control plane) and `tenant:coulomb` (the +ecosystem-development tenant this workstation already operates under). + +This closes **Gap #1** flagged in `qonto-assistant`'s +`workplans/QONTO-WP-0003-mcp-surface.md` closure: no OIDC issuer existed +anywhere in the fleet, so `qonto-assistant`'s MCP surface shipped with an +interim shared-secret bearer token instead of real workload identity. +`key-cape` is "Implementation complete (v0.1)" and already implements the +profile's `client_credentials`/workload-identity grant for service accounts +— it's the component that should close this gap, not `user-engine` +(downstream user/profile data layer, not an identity provider) or +`identity-canon` (terminology research only). + +**Depends on:** `key-cape` v0.1 (done, `KEY-WP-0001`), NetKingdom IAM Profile +v0.2 tenant + service-account claim rules (already ratified canon — no new +profile work needed, this is provisioning against an existing contract), and +the cross-repo runtime direction now tracked in +`railiance-master/workplans/RMASTER-WP-0019-knative-qonto-runtime-on-reef-railiance.md`. + +This workplan no longer targets a permanent direct `qonto-assistant` +deployment shape. Its workload-identity lane is intended for the future +`rapp-qonto` runtime on `rail-knative` hosted by `reef-railiance`, with +current `qonto-assistant` consumption allowed only as a migration bridge until +that package/rail path exists. + +## Task: Confirm tenant identifier and provision the tenant plane + +```task +id: KEY-WP-0004-T01 +status: done +priority: high +state_hub_task_id: "c3bdbdb6-6b3f-51b5-8521-d67f113f1c3d" +``` + +**Decided 2026-07-23 (Bernd):** the IAM profile's `tenant:customer:` / +`tenant:sandbox:` suggested identifiers name a tenant by **capability +role** (customer, vendor, ...) — but tenants already known to hold multiple +roles at once (see `net-kingdom/docs/princedom-isolation-exploration.md`'s +unratified `PLTF`/`IAM`/`VEN`/`CUS` role model; Binky itself is both `CUS` +and, later, `VEN`). Using a role word as the tenant *grouping* would collide +with that separate role dimension. Decision: classify tenants by an +onboarding-risk / entity-shape **grouping**, orthogonal to capability role: + +```text +trial — test/trial/showcase tenants only +friendly — known, easily reached, tolerant of experimentation/instability +single — one-person business entities (freelance consultants) +small — up to 10 employees at time of onboarding (attoo) +medium — up to 100 employees (attoo) +large — up to 1000 employees (attoo) +enterprise — 1001+ employees (attoo) +consumer — private individuals +family — a legal family +community — a non-legal group of people +association — a legal association of people +agentic — financially enabled AI entities +``` + +`tenant::` replaces `tenant:customer:` / +`tenant:sandbox:` as the identifier shape. **Binky maps to `friendly`** +— known, reachable, tolerant of early instability — giving +**`tenant:friendly:binky`** (slug matches the OpenBao path already in +production, `tenants/binky/qonto-api`). + +**Open question, not yet resolved by this decision:** this grouping list +classifies tenants as external-shaped entities: it has no obvious slot for +`tenant:coulomb` itself (the platform's own internal/reference tenant, +ADR-0006) — none of `trial`/`friendly`/`single`/.../`agentic` fit "the +platform building itself." Confirm with Bernd whether `tenant:coulomb` stays +an ungrouped special case (alongside `tenant:platform`) or gets folded into +this scheme somehow, before treating the grouping list as complete. + +**Also open:** this is a fleet-wide vocabulary decision — the ratified +`net-kingdom/canon/standards/iam-profile_v0.2.md` "Tenant Claim" section +still documents the old `customer`/`sandbox` suggested identifiers, and +NetKingdom owns that canon per ADR-0011. Recording this grouping list only +in this key-cape workplan lets it drift from canon. Recommend a follow-up: +either a decision record or an ADR in `net-kingdom` adopting this grouping +list as the tenant-identifier standard, superseding the old suggested +identifiers. Not drafted here — flagging so it isn't lost. + +Provision whatever tenant/org-unit boundary key-cape's current backend +(LLDAP/Authelia) needs so tokens issued for subjects under this tenant +correctly carry `tenant:friendly:binky` and nothing implies platform-root +authority for it (profile rule: tenant administration for a tenant must +never imply platform-root authority). + +Done when: `tenant:coulomb`'s place in the grouping scheme is resolved; the +`net-kingdom` canon-update question above is answered (drafted or explicitly +deferred); a test-subject token carries the `tenant:friendly:binky` claim. + +2026-07-27: IAM Profile v0.2 now preserves `tenant:platform` and +`tenant:coulomb` as reserved ungrouped tenants and ratifies the external +grouping vocabulary. The live service-token proof carries +`tenant:friendly:binky` without platform authority. + +## Task: Provision bernd.worsch@binky-hedgehog.com as tenant-admin + +```task +id: KEY-WP-0004-T02 +status: done +priority: high +state_hub_task_id: "202c419e-838f-5c2f-8a52-9b1c36a6d6b9" +``` + +Create the user in key-cape's current backend, enroll MFA per profile +requirements (`assurance` claim, `net-kingdom/canon/standards/iam-profile_v0.2.md`), +and assign a tenant-admin role/group scoped to `tenant:friendly:binky` only. + +2026-07-27 direction update: do not complete this through a one-shot operator +script. `USER-WP-0020` and `NK-WP-0023` now own a reusable self-service and +administration portal plus NetKingdom provisioning adapters. This task is +their first production acceptance case. + +The `binky-hedgehog.com` mailbox must exist and be reachable for account +verification, but mailbox availability is now an input to the reusable +registration flow rather than a manual provisioning procedure. + +Done when: `bernd.worsch@binky-hedgehog.com` completes OIDC/PKCE + MFA login +and receives a token carrying `tenant:friendly:binky` and a tenant-admin role +claim; the same token does **not** grant access to `tenant:platform` or any +other tenant. + +2026-07-28: user-engine's deployed portal API created the tenant-admin +membership and provisioned/linked the LLDAP identity through the reusable +NetKingdom adapter. The identity is intentionally in +`password_setup_required`; LLDAP has no SMTP configuration for its native +reset delivery. First-password handoff, MFA enrollment, and final human-token +claim/denial evidence remain. No operator-set password or raw credential was +used as a shortcut. + +2026-07-28 update: the reusable password handoff is now live at the canonical +KeyCape host and is issued/renewed through the user-engine administration UI. +Links are opaque, expire after 15 minutes, are single use, and older links are +revoked on renewal. A disposable-user live test proved password registration, +directory login, replay denial, and cleanup. This task now awaits only the +Binky user's own password choice, MFA enrollment, and final scoped-token +acceptance. + +2026-07-29: the first portal attempt correctly denied cross-tenant access +because KeyCape had not projected the established `net-kingdom-admins` group +to the new platform-operator role. Commit `90a2078` adds and tests the explicit +`tenant:platform` / `platform-operator` compatibility mapping and is deployed +Ready. The operator must start a fresh OIDC session so the corrected claims +are minted. + +2026-07-29 human acceptance: the Binky administrator completed the reusable +password setup, enrolled a privacyIDEA TOTP factor, and successfully signed in +through KeyCape with password plus OTP. Enrollment revealed that privacyIDEA's +default QR label exposed only the token serial. The live +`coulomb-friendly-token-labels` enrollment policy now emits issuer `Coulomb` +and label `{user}@{realm}` for future tokens; existing authenticator entries +must be renamed locally because wallets do not accept remote label updates. +The live token then carried `tenant:friendly:binky`, roles `user` and +`tenant-admin`, only the Binky users/admins groups, and AAL2 assurance with +`pwd` + `otp` and `mfa: true`. The same session was denied access to both +`tenant:platform` and `tenant:coulomb` administration. Acceptance complete. + +## Task: Register a workload-identity OIDC client for the qonto runtime + +```task +id: KEY-WP-0004-T03 +status: done +priority: high +state_hub_task_id: "0d3ccd35-bad5-5c30-95a3-61fce584425d" +``` + +Register an OIDC client per the profile's service-account/workload-identity +flow (`client_credentials` or the documented workload-token-exchange +equivalent — §"Service Account Flow"), scoped to `tenant:friendly:binky`, +with the minimum role/scope the qonto runtime surface actually needs. The +intended steady-state consumer is the future `rapp-qonto` runtime on +`rail-knative`; current `qonto-assistant` callers may use the same lane only +as a documented migration bridge until that package split exists. No +interactive login for this client — it's for agent-harness sessions and other +automated callers, not humans. + +The resulting `client_secret` must never be pasted into chat, Git, this +workplan, or a State Hub message or progress event — hand-off happens only +through T04's secrets-engine/OpenBao lane. + +Done when: a `client_credentials` token exchange against key-cape succeeds +for this client and produces a short-lived service token carrying +`tenant:friendly:binky`; the `client_id`, granted scopes, and intended future +binding to `rapp-qonto` are documented here; no secret material appears +anywhere in this repo's history. + +2026-07-26: Added `docs/qonto-runtime-identity-contract.md` with stable +principal/client names, least-privilege claims, non-secret evidence, and the +automation/custody boundary. Actual client issuance remains open. + +2026-07-27: `KEY-WP-0006` delivered `rapp-qonto-client`. Live exchange +produced only the documented tenant, role, and `qonto:read` scope; wrong +secret and excessive scope were denied. + +## Task: Route client_secret custody through secrets-engine + OpenBao + +```task +id: KEY-WP-0004-T04 +status: done +priority: high +state_hub_task_id: "9cb09dbf-1fd0-55c5-9c45-b3b7296ecd91" +``` + +Coordinate with `secrets-engine` to create a scoped OpenBao lane for this +`client_secret`, mirroring the existing `tenants/binky/qonto-api` pattern +already in production use for the Qonto bank credential itself. The steady +target is a dedicated future runtime role for `rapp-qonto`; if a temporary +bridge via the current `qonto-assistant` runtime role is needed before the +package split lands, it must be recorded as temporary rather than treated as +the final contract. + +Done when: the intended qonto runtime role can fetch the `client_secret` via +`secrets-engine`/OpenBao; any temporary `qonto-assistant` bridge is explicitly +marked transitional; Bernd never handles the raw value at any point in this +flow. + +2026-07-26: `warden plan` classified first-time provisioning as one explicit +founder authority act through `warden desk`. No secret was requested or read. +All repeatable verification and revocation work is designated for sanctioned +agent execution and later functional automation. + +2026-07-27: Custody is established at +`platform/workloads/rapp-qonto/keycape-client`, with runtime injection through +`sso/keycape-rapp-qonto-client`. No raw value entered evidence or human +handling. + +## Task: Register the credential lane with ops-warden + +```task +id: KEY-WP-0004-T05 +status: done +priority: high +state_hub_task_id: "39f1f803-732e-5905-97d9-c7493c66c8ee" +``` + +This is the task that directly minimizes Bernd's ongoing engagement: add an +entry to ops-warden's routing catalog (`registry/routing/catalog.yaml`) +pointing at this new OIDC client / login lane, following the pattern already +documented in `ops-warden/SCOPE.md`'s "Issue vs route" table ("Login / OIDC / +MFA → key-cape / Keycloak → Assist — route; proxy `login` lane when +`exec_capable`"). ops-warden does not mint tokens or take custody here — it +routes/assists, same as every other credential lane it fronts. The lane should +describe the future `rapp-qonto` runtime path on `rail-knative`, not only the +current source repo. + +Once the OpenBao lane from T04 exists, run ops-warden's workload security +posture conformance checker +(`ops-warden/scripts/check_secret_posture_conformance.py`) against the qonto +runtime workload that will later become `rapp-qonto`; until the split lands, +`qonto-assistant` may be used as the migration subject if that temporary state +is recorded. Confirm it meets the expected maturity tier (dev/test/prod +posture, M0–M3 workload maturity) before this goes live with real +credentials, and record the result (pass or documented exception). + +Done when: `warden route show` (or equivalent) resolves this lane correctly; +posture conformance check passes or exceptions are explicitly recorded; from +this point on, discovering and using this credential lane needs `warden +access`, not direct key-cape/OpenBao admin knowledge. + +2026-07-26: Routing requirements and required conformance evidence are now +documented. Catalog publication waits for the real client and OpenBao role so +the route does not advertise an unusable lane. + +2026-07-27: Published route `rapp-qonto-keycape-client`; route resolution and +the metadata-only M3/prod posture check both pass. + +## Task: automation@binky-hedgehog.com service identity (deferred) + +```task +id: KEY-WP-0004-T06 +status: cancel +priority: low +state_hub_task_id: "fbee11fc-7a60-5dd7-8522-f80ddb4aea55" +``` + +Forward-looking per Bernd's framing ("I guess it will later be helpful") — +**do not provision until a concrete automation workflow actually needs its +own identity** distinct from the qonto runtime workload client from T03. +Dormant service-account credentials are themselves a posture risk (unused +attack surface, nothing to rotate against), so this stays parked rather than +front-loaded. + +When a real consuming workflow exists: same pattern as T02 but scoped to an +automation-appropriate role (not tenant-admin), likely paired with its own +`client_credentials` registration for whichever agent/service authenticates +as it, following T03/T04/T05's pattern rather than inventing a new one. + +Done when: explicitly triggered by a named consuming workflow — until then +this task stays `todo` and does not block closing the rest of this workplan. + +## Task: Closure review + +```task +id: KEY-WP-0004-T07 +status: done +priority: low +state_hub_task_id: "77b0556f-62fd-5046-9ba1-718246151a6f" +``` + +Mark workplan finished when T01–T05 are done (T06 may legitimately remain +`todo`/parked by design — see its own task note). Confirm end to end: a +human token (bernd.worsch@binky-hedgehog.com, tenant-admin) and a service +token (future qonto runtime client, with any temporary `qonto-assistant` +bridge explicitly recorded) both resolve to `tenant:friendly:binky` and +nothing else; the credential lane is discoverable via `warden access` without +Bernd touching key-cape/OpenBao admin surfaces directly. The runtime follow-up +then continues in `QONTO-WP-0004` and `RMASTER-WP-0019`. Run +`statehub fix-consistency`. + +2026-07-29 closure: T01–T05 are complete, T06 is intentionally cancelled, +the human Binky flow passed AAL2 and tenant-isolation acceptance, and the qonto +workload client passed its positive-scope and negative credential/scope checks. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0005-iam-profile-core-claims.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0005-iam-profile-core-claims.md new file mode 100644 index 00000000..648d2405 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0005-iam-profile-core-claims.md @@ -0,0 +1,245 @@ +--- +id: KEY-WP-0005 +type: workplan +title: "IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-07-23" +updated: "2026-07-23" +state_hub_workstream_id: "711a8303-69aa-5db6-9043-92530c6dc81b" +--- + +# IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles + +Discovered while planning `tenant-engine/workplans/TEN-WP-0003-flex-auth-write-authorizer.md`: +`key-cape`'s token issuance (`src/internal/server/oidc/token.go`) does not +currently emit `tenant`, `principal_type`, `groups`, `roles`, or +`assurance` — none of `net-kingdom/canon/standards/iam-profile_v0.3.md`'s +Core Claims are implemented yet, only `preferred_username`/`email`/`groups` +(LDAP-sourced, not the profile's normalized `groups` claim) and OIDC's own +base claims (`nonce`, etc.). + +This is bigger than "add the `tenant_roles` claim on top of existing tenant +support" — `key-cape` isn't yet profile-conformant at all on the tenant/ +principal/assurance axis. `tenant_roles` (the reason this workplan exists) +is the last, optional claim in that same set (v0.3's addition on top of +v0.2's baseline) — implementing it in isolation without the claims it +depends on for meaning (`tenant`, `principal_type`) would be incoherent. + +**This workplan is registered, not implemented, by the same session that +found the gap.** Token issuance is security-sensitive code in a Go codebase +with its own adapter conventions (`internal/adapters/{lldap,privacyidea,authelia}`) +that deserves dedicated context before changing, not a rushed edit from a +first read of `token.go`. Scoped and sequenced here so the work isn't lost. + +**Depends on:** `net-kingdom/canon/standards/iam-profile_v0.3.md` (ratified). +**Related:** `tenant-engine/workplans/TEN-WP-0003-flex-auth-write-authorizer.md` +(the consumer waiting on `tenant_roles`); `key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md` +(Binky's onboarding, which needs `tenant` claim support to produce a +correct `tenant:friendly:binky` token at all). + +## Task: Emit `tenant`, `principal_type`, `groups`, `roles`, `assurance` + +```task +id: KEY-WP-0005-T01 +status: done +priority: high +state_hub_task_id: "32d5fbdc-cd2b-5bc8-9d2b-1a410b55e806" +``` + +Bring `token.go`'s claim assembly up to `iam-profile_v0.3.md`'s Core Claims +table baseline (the v0.2-originated set): + +- `tenant`: resolve from whatever user/service/agent-to-tenant mapping + `key-cape`'s LLDAP/Authelia backend can provide today; if no such mapping + exists yet, that's itself a sub-task — this claim cannot be stamped from + nothing. +- `principal_type`: `human` for the existing Authorization Code + PKCE flow; + `service` for client-credentials flows (check whether `key-cape` already + issues client-credentials tokens at all — `grant_types_supported` in the + discovery contract claims it does, but that needs verifying against real + code, the same way this workplan verified `tenant` claim absence). +- `groups`, `roles`: normalize from LDAP group/role data into the profile's + flat string-array shape. +- `assurance`: `{level, methods, mfa, source, at}` per the profile — + `privacyIDEA`-backed MFA evidence maps to `aal2`; single-factor maps to + `aal1`. + +Done when: `tools/iam-profile-conformance/` (already exists) passes against +these claims for both human and service token issuance paths. + +**Done 2026-07-24, human flow only — scope split made explicit, not +silently narrowed:** verified first (as flagged): `grant_types_supported` +advertises `client_credentials` in `discovery.go`, but `token.go`'s +`ServeHTTP` only ever accepted `grant_type == "authorization_code"` — there +is no service-token issuance path at all, so "verifying against service +token issuance" isn't possible until that path exists. Building a +`client_credentials` grant handler from scratch is a distinct, materially +larger piece of work than extending the existing human flow (new endpoint +logic, new client-authentication semantics, no existing tests to extend) +— **not attempted in this pass**, left open below rather than declared done. + +What shipped for the human Authorization Code + PKCE flow: + +- `domain.User.Tenant` (new field, `omitempty`, empty-safe for existing + YAML configs) + `token.go`'s `effectiveTenant()`: falls back to + `tenant:coulomb` (this workstation's actual current tenant, ADR-0006) + when unset — never emits an empty `tenant` claim, never silently assigns + a user to a tenant they weren't given. +- `principal_type: "human"`, unconditional. +- `groups`/`roles`: promoted from scope-gated (only `groups`, only with the + `groups` scope) to unconditional core claims, always present as `[]` + when empty, never `null` (`nonNilStrings()`). One pre-existing test + asserted the old scope-gated behavior for `groups` + (`TestTokenHandler_ScopeFiltering_ProfileScope`) — updated to assert the + new, intentional behavior, not silently left failing or reverted. +- `assurance`: built from whether MFA was *actually verified in this + session* (`PKCESession.MFAVerified`, new field, threaded through + `completeAuthorization`'s two call sites in `authorize.go`), not from + static enrollment state — `aal2` only when MFA was required and passed + this time, `aal1` otherwise. + +Verified for real: `go build ./...` and `go vet ./...` clean; `go test +./...` green across the whole repo (one pre-existing test updated to match +the new intentional behavior, described above, not silently broken). Two +new `authorize_test.go` cases directly assert `PKCESession.MFAVerified` is +set correctly on both the MFA-required and MFA-not-required paths. +`tests/profile/profile_test.go`'s `TestCompleteTokenFlow` — the repo's own +full HTTP integration test (real server, real PKCE flow, real JWT decode) +— extended with real assertions for all five new/changed claims, not just +presence checks: `tenant == "tenant:coulomb"` (the fallback path, since +the test's `testuser` has no explicit tenant), `principal_type == "human"`, +`groups == ["developers"]`, `roles == []`, and +`assurance == {level: "aal1", mfa: false, source: "key-cape", ...}` (this +test's `mockMFA` never requires MFA, so `aal1` is the correct expected +value, not `aal2`). + +Not run: `net-kingdom/tools/iam-profile-conformance`'s Python conformance +tool against a live minted token — it needs a reachable issuer, which would +mean standing up the full `docker-compose.dev.yml` stack +(Authelia+LLDAP+privacyIDEA) beyond what's practical in this pass. The +equivalent claim-shape and value checks it would perform are covered by +`TestCompleteTokenFlow`'s real HTTP round trip instead — not a like-for-like +substitute, but real coverage, not an assumption. + +**Explicitly still open** (not this task, not silently dropped): +`client_credentials` grant handling — service-token issuance for `key-cape` +callers with no human in the loop (`tenant-engine`'s own eventual key-cape +integration, and any other service caller). Needs its own scoped follow-up: +a new `principal_type: "service"` code path in `token.go` (or a sibling +handler), client secret/authentication semantics, and its own test suite — +this workplan's `T02` (`tenant_roles`) doesn't strictly require it, since +`tenant_roles` attaches to whatever principal_type a token already carries, +human included. + +## Task: Emit `tenant_roles` (optional, cached) + +```task +id: KEY-WP-0005-T02 +status: done +priority: high +state_hub_task_id: "c3ee6021-b28d-50a5-be60-ecb5631f56e9" +``` + +At token-issuance time, call `tenant-engine`'s cache-read endpoint +(`GET /tenants/{tenant_id}/roles`) using the `tenant` claim resolved in T01, +and stamp the result as the optional `tenant_roles` claim +(`iam-profile_v0.3.md`, "Tenant Roles" section). Follows the same adapter +pattern as `internal/adapters/{lldap,privacyidea,authelia}` — a new +`internal/adapters/tenantengine` package, not inline HTTP calls in +`token.go`. + +Fail open on this claim specifically, not closed: `tenant_roles` is +documented as a cache callers must not trust for privileged decisions +anyway (`flex-auth` re-validates live). If `tenant-engine` is unreachable at +issuance time, omit the claim rather than fail the entire token issuance — +losing a performance optimization is acceptable; blocking login because a +cache source is down is not. This is the one place in the whole `tenant_roles` +design where fail-open is correct, precisely because the live-lookup path +exists as the real gate. + +Done when: token issuance still succeeds with `tenant_roles` omitted when +`tenant-engine` is unreachable (test simulates the outage); present and +correct when reachable, for a tenant with known role grants. + +**Done 2026-07-24:** New `internal/adapters/tenantengine` package +(`Client.Roles(ctx, tenantID) ([]string, bool)`), mirroring +`internal/adapters/{lldap,privacyidea,authelia}`'s shape exactly. Fails +open by construction — unreachable, non-200, or malformed body all return +`(nil, false)`, never an error the caller has to specially handle; a `nil +*Client` also fails open safely rather than panicking, so `TokenHandler` +doesn't need a separate "is this configured" branch. Wired into +`TokenHandler.TenantEngine` (nil by default — existing tests and call +sites are unaffected); `token.go` stamps `tenant_roles` only when `ok`. + +7 adapter-level tests (success, non-200, malformed body, connection +failure, timeout, nil client, empty base URL). 3 new `token_test.go` cases +prove the actual required behavior end-to-end through `TokenHandler`, not +just at the adapter layer: present and correct when reachable; **token +issuance still returns 200 with every other required core claim intact** +when unreachable, `tenant_roles` simply absent (the literal done-criteria); +absent when `TenantEngine` isn't configured at all. `go build`/`vet` +clean, `go test ./...` green repo-wide. + +**A real bug found and fixed along the way, not worked around:** the first +live cross-process check (real `flex-auth`, real `tenant-engine`, this +adapter) returned `tenant_not_found` for a tenant that genuinely existed. +`tenant-engine`'s `GET /tenants/{tenant_id}/roles` was keyed by its +internal `tenant_id`, but `key-cape` (like any external caller) only ever +has the tenant's profile *identifier* (the `tenant` claim value) — it has +no way to know tenant-engine's internal id. Fixed at the source: +`tenant-engine/workplans/ADHOC-2026-07-24.md` (identifier-or-id resolution +added to `InMemoryTenantStore`), re-verified with the exact same live +three-process chain afterward — `roles=[IAM] ok=true`, resolved by +identifier. This is exactly why T02 insisted on a live-server-reachable +test rather than only an `httptest` fake: the fake would have used +whatever id shape the test author happened to pick and never caught this. + +## Task: Closure review + +```task +id: KEY-WP-0005-T03 +status: done +priority: low +state_hub_task_id: "c88a3457-2964-5c44-8220-76f82df3a753" +``` + +Confirm T01–T02 done; conformance suite green for both tasks together. +Note in closure: this unblocks `KEY-WP-0004`'s Binky onboarding actually +producing conformant tokens, and unblocks `flex-auth`'s side of the +`tenant_roles` picture receiving a real claim to reason about (though +`flex-auth`'s live-lookup path doesn't depend on this claim existing — only +the cache-read/performance path does). Run `statehub fix-consistency`. + +**Closed 2026-07-24.** T01–T02 done. `go build ./...`, `go vet ./...` clean; +`go test ./...` green across the whole repo. Python +`net-kingdom/tools/iam-profile-conformance` not run against a live instance +(needs the full Authelia+LLDAP+privacyIDEA stack, impractical in this +pass) — real coverage instead came from `tests/profile`'s own full HTTP +integration test (`TestCompleteTokenFlow`) with genuine claim-value +assertions, and from three separate live cross-process checks against a +real `tenant-engine` (one of which found and fixed a real bug in +`tenant-engine` itself, `ADHOC-2026-07-24`). + +**What this closes:** `key-cape`'s human Authorization Code + PKCE flow now +emits every IAM Profile v0.3 core claim (`tenant`, `principal_type`, +`groups`, `roles`, `assurance`) plus the optional `tenant_roles` cache, +correctly sourced from a real `assurance` signal (MFA actually verified +this session, not static enrollment) and a real `tenant-engine` call +(fail-open, never blocking login). `KEY-WP-0004`'s Binky onboarding can now +produce conformant tokens for human logins. + +**What stays explicitly open, not silently dropped:** +- `client_credentials` / service-token issuance — no such flow exists in + `token.go` at all yet (confirmed in T01); needed before any *service* + caller (not a human) can get a token with `principal_type: "service"` and + its own `tenant`/`tenant_roles`. A materially larger, separate piece of + work than either task in this workplan. +- `flex-auth`'s side of `tenant_roles` (`FLEX-WP-0008`, already closed + separately) doesn't depend on this claim existing at all — its + live-lookup path calls `tenant-engine` directly, so this workplan doesn't + block it, but also doesn't complete the full `tenant_roles` picture on + its own until service tokens exist too. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0006-client-credentials-service-tokens.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0006-client-credentials-service-tokens.md new file mode 100644 index 00000000..632c4c2c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0006-client-credentials-service-tokens.md @@ -0,0 +1,116 @@ +--- +id: KEY-WP-0006 +type: workplan +title: "Client credentials and service-token issuance" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-07-27" +updated: "2026-07-27" +state_hub_workstream_id: "d8d20d8f-61e0-54e2-9a88-a3958f0b6f2d" +--- + +# KEY-WP-0006 - Client credentials and service tokens + +Implement the missing service-principal half of the IAM Profile so +`KEY-WP-0004` can issue a least-privilege identity to `rapp-qonto`. + +## T01 - Model confidential service clients + +```task +id: KEY-WP-0006-T01 +status: done +priority: high +state_hub_task_id: "993913a7-f951-50ee-9b10-d5b9e67b523c" +``` + +Extend static client registration with an explicit service subject, tenant, +roles, and an environment-backed secret reference. Refuse startup when a +`client_credentials` client is public, lacks its identity fields, or cannot +resolve its secret. Never serialize or log the resolved secret. + +2026-07-27: Added explicit service subject, tenant, roles, and in-memory-only +resolved secret fields. Configuration validation rejects public or incomplete +service clients, and startup resolves only `env:` secret references while +reporting the variable name—not its value—on failure. + +## T02 - Implement secure client_credentials exchange + +```task +id: KEY-WP-0006-T02 +status: done +priority: high +state_hub_task_id: "801a6d55-2f91-51b1-88e4-cc7f0867238d" +``` + +Accept confidential client authentication through HTTP Basic, compare secrets +in constant time, restrict requested scopes to the registered allowlist, and +issue a short-lived access token with `principal_type=service`, +tenant-specific subject/roles, empty groups, `aal1` client-secret assurance, +and optional cached `tenant_roles`. Do not issue an ID token for this +non-human grant. + +2026-07-27: Implemented HTTP Basic confidential-client authentication with +fixed-length SHA-256 constant-time comparison, allowlisted scopes, short-lived +service claims, optional tenant-role cache, token-issued telemetry, and no ID +token. + +## T03 - Prove positive and negative conformance + +```task +id: KEY-WP-0006-T03 +status: done +priority: high +state_hub_task_id: "e5c2a8b1-0879-57fa-a197-250a34c75bdb" +``` + +Cover valid exchange, unknown client, public client, missing/wrong secret, +unsupported grant, excess scope, tenant isolation, telemetry redaction, and +discovery metadata. Run the full Go build, vet, and test suite. + +2026-07-27: Added positive service-claim and negative wrong-secret/excess-scope +tests, updated discovery to advertise only implemented grants/auth methods, +and verified `go test ./...`, `go vet ./...`, and `go build ./...` across the +module. + +## T04 - Provision and verify rapp-qonto + +```task +id: KEY-WP-0006-T04 +status: done +priority: high +state_hub_task_id: "31af6b8c-96ff-5060-87db-3803b9a9dd12" +``` + +Generate the client secret without disclosure, store it through the approved +OpenBao lane, register the static `rapp-qonto` client, deploy KeyCape, and +verify a real exchange yields only the documented Binky service claims. +Publish non-secret evidence to `KEY-WP-0004-T03` through T05. + +2026-07-27: Provisioned the confidential client secret at +`platform/workloads/rapp-qonto/keycape-client`, injected it into +`sso/keycape-rapp-qonto-client`, registered `rapp-qonto-client`, and deployed +image `main-e877d27-2` on railiance01. A real exchange produced only +`sub=rapp-qonto`, `tenant=tenant:friendly:binky`, `principal_type=service`, +`roles=[qonto-reader]`, empty groups, and `scope=qonto:read`; excessive scope +and a wrong secret were denied. Public DNS still resolves +`kc.coulomb.social` to the older CoulombCore endpoint, so the deployment proof +used TLS-preserving direct resolution to railiance01 (`92.205.62.239`). + +## T05 - Closure review + +```task +id: KEY-WP-0006-T05 +status: done +priority: low +state_hub_task_id: "8c72d7d9-4086-5e64-b939-a12bff076124" +``` + +Close after T01-T04 pass and the repeatable verification path is documented. + +2026-07-27: Closure review passed. Build, vet, unit/profile tests, live +positive claims, negative authentication/scope checks, and discovery metadata +all passed. The stale public DNS target is handed off as routing work rather +than weakening the service-token contract. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0007-user-engine-portal-oidc-client.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0007-user-engine-portal-oidc-client.md new file mode 100644 index 00000000..f1b04e1a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0007-user-engine-portal-oidc-client.md @@ -0,0 +1,81 @@ +--- +id: KEY-WP-0007 +type: workplan +title: "User-engine portal OIDC relying-party integration" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-07-27" +updated: "2026-07-29" +depends_on: + - KEY-WP-0005 + - USER-WP-0020 +state_hub_workstream_id: "24db4178-705c-5b09-b7db-ee7a345d71a0" +--- + +# KEY-WP-0007 - User-engine portal OIDC relying-party integration + +Register and prove the reusable user-engine portal as a strict KeyCape public +client. This is the authentication edge required by `NK-WP-0023-T03`; it does +not move user-domain or authorization ownership into KeyCape. + +## Register the static portal client + +```task +id: KEY-WP-0007-T01 +status: done +priority: high +state_hub_task_id: "7d722cae-bf6d-52e0-80f4-cc51e9fb42f0" +``` + +Register `user-engine-portal` for authorization code with mandatory S256 PKCE, +exact callback URIs and only `openid profile email groups`. No wildcard, +implicit flow, client secret, or dynamic registration is allowed. + +## Deploy and verify the live client + +```task +id: KEY-WP-0007-T02 +status: done +priority: high +state_hub_task_id: "0b222062-4352-5ae6-9e85-95787af08409" +``` + +Update the railiance01 KeyCape configuration without exposing its signing key +or backend credentials. Verify discovery, authorize redirect validation, +unregistered callback denial and successful token exchange through the portal. + +## Prove claims and MFA isolation + +```task +id: KEY-WP-0007-T03 +status: done +priority: high +state_hub_task_id: "859e7322-71dc-58cc-9f44-6f869c2d0b59" +``` + +Prove issuer, audience, tenant, groups, roles and assurance claims are verified +by the portal and that tenant administration does not imply platform-root. +Complete the Binky user/MFA acceptance through the reusable browser path. + +2026-07-27: The live client accepts only the exact portal callback and rejects +an unregistered callback with `invalid_profile_usage`. The portal begins an +S256 PKCE flow and hands authentication to Authelia. LLDAP tenant envelope +mapping now recognizes unambiguous `tenant:::users|admins` groups; +ambiguous multi-tenant directory envelopes fail closed to no explicit tenant. + +2026-07-28: suspension now fails closed at both token exchange and +`/userinfo`; the deployed `key-cape:909bb32` image refuses subjects carrying +the `netkingdom-suspended` directory group. The portal admin surface uses this +through the provider-neutral lifecycle API. The human Binky MFA/claims +acceptance remains the final part of this task. + +2026-07-29 complete: the reusable browser path issued the Binky password +handoff, completed privacyIDEA TOTP enrollment, and returned through the +portal authorization-code flow. `/api/v1/me` verified the canonical issuer, +`user-engine-portal` audience, `tenant:friendly:binky`, Binky-only groups, +roles `user` + `tenant-admin`, and AAL2 assurance with `pwd` + `otp` and +`mfa: true`. The same session was denied administration of +`tenant:platform` and `tenant:coulomb`. All workplan tasks are complete. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md new file mode 100644 index 00000000..438ab548 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md @@ -0,0 +1,118 @@ +--- +id: KEY-WP-0008 +type: workplan +title: "Registration handoff and client-aware MFA policy" +domain: infotech +repo: key-cape +status: finished +owner: grok +topic_slug: netkingdom +created: "2026-08-09" +updated: "2026-08-16" +depends_on: + - NK-WP-0025 +state_hub_workstream_id: "23eb67d4-cf10-57a1-92f6-90146a294bda" +--- + +# KEY-WP-0008 - registration handoff and client-aware MFA + +Let a registered OIDC client offer NetKingdom signup and request step-up +without making KeyCape an account store or weakening high-assurance clients. + +## T01 - Add a safe registration handoff + +```task +id: KEY-WP-0008-T01 +status: done +priority: high +state_hub_task_id: "e5fb4d73-1e19-565c-80d5-e0eee274ce63" +``` + +Define an allow-listed registration URL for eligible clients and preserve +client ID, redirect URI, PKCE intent, tenant hint, and return context in a +signed, expiring state envelope. Registration completion must restart the +normal authorization flow and must not mint a token directly. + +Done when unknown users can choose signup from an eligible authorization flow +without open redirect, client substitution, or state replay. + +Implemented `prompt=create` and `/authorize/register` against the client's +static `registrationUrl`, plus HMAC-signed `kc_handoff` envelopes consumed +once at `/authorize/return`. Return restarts `/authorize` and never mints a +code. Ineligible clients get no signup link. Live registration entry remains +user-engine-owned per NK-WP-0025; KeyCape only issues the return envelope. + +## T02 - Replace global MFA with client-aware minimum assurance + +```task +id: KEY-WP-0008-T02 +status: done +priority: high +state_hub_task_id: "1ba50454-b6f5-578e-a526-da74b8082a89" +``` + +Replace the single require-for-all switch with policy that combines client +minimum assurance, requested ACR/step-up, tenant policy, protected action, and +current session assurance. Preserve mandatory MFA for platform/admin clients. +Allow coulomb-social ordinary login at password assurance when no stronger +rule applies. + +Done when one low-assurance client cannot suppress MFA for another client or +reuse an under-assured session for a high-assurance request. + +Implemented with nullable per-client `mfaRequired`: an explicit client value +overrides the provider default only for that client. Absent values preserve +the existing provider-driven policy. + +2026-08-16: `DecideAssurance` now combines client minimum, requested ACR, +provider default, `max_age`, `prompt=login`, and current KeyCape login-session +level. An AAL1 session cannot satisfy an AAL2 client or `acr_values=aal2`. +`coulomb-social` in `config/dev-config.yaml` is `mfaRequired: false`; other +clients keep the provider default. Users without an enrolled factor are sent +to the client's `enrollmentUrl` instead of completing authorization. + +## T03 - Support explicit step-up and fresh authentication + +```task +id: KEY-WP-0008-T03 +status: done +priority: high +state_hub_task_id: "f7acbc12-e597-5d1e-8b29-bfa02237730f" +``` + +Implement supported ACR/max-age or equivalent IAM Profile parameters, invoke +privacyIDEA only when policy requires it, and return verifiable assurance +claims. Handle users without an enrolled factor through a safe enrollment +handoff rather than an authorization bypass. + +Done when coulomb.social can request MFA for a profile/action and verify the +result from token claims. + +Implemented `acr_values` preservation and AAL2/MFA forcing through the +privacyIDEA challenge. The existing token `assurance` claim reports `aal2` +and `mfa: true` only after successful verification. + +## T04 - Prove policy isolation and compatibility + +```task +id: KEY-WP-0008-T04 +status: done +priority: high +state_hub_task_id: "bc660767-387e-59a5-bfce-caccf4082c08" +``` + +Test known and unknown users, registration link eligibility, state expiry and +replay, password-only coulomb-social login, profile/action step-up, no-factor +enrollment, OpenBao mandatory MFA, cross-client session reuse, and logout. +Keep static client registration and exact redirect rules unchanged. + +Done when existing high-assurance clients pass unchanged and the new +coulomb-social journey passes live. + +2026-08-16: isolation tests cover known/unknown users, registration +eligibility, state expiry/replay, password-only coulomb-social, ACR step-up, +no-factor enrollment handoff, OpenBao mandatory MFA, cross-client AAL1 +session reuse, logout, and exact redirect enforcement. Full Go suite passes. +Live coulomb-social AAL1/AAL2 isolation was already proven on railiance01 +under NK-WP-0025-T05 (2026-08-14); this closeout adds the KeyCape-side +regression suite and `/logout`. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md new file mode 100644 index 00000000..c9e585db --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md @@ -0,0 +1,138 @@ +--- +id: KEY-WP-0009 +type: workplan +title: "Provider capability declarations and bounded service identities" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-08-23" +updated: "2026-09-27" +depends_on: + - NK-WP-0030 + - KEY-WP-0006 +state_hub_workstream_id: "c1a9b1cc-2ff0-566a-b544-1a2ef967fc0d" +--- + +# KEY-WP-0009 — Provider capabilities and bounded service identities + +Publish KeyCape-owned security-scenario interfaces and make the existing +service-token issuer precise enough for OpenBao machine-login consumers. This +work accepts identity issuance ownership without taking over OpenBao roles, +policies, secret custody, or privacyIDEA token lifecycle. + +## Publish C1 and C2b provider declarations + +```task +id: KEY-WP-0009-T01 +status: done +priority: high +state_hub_task_id: "eaf7af48-b3d6-5f85-b0ce-ff2b640f9cc3" +``` + +Publish Playbook Capability Contract v0.1 declarations for the KeyCape C1 +runtime and its privacyIDEA-backed C2b integration. Name exact entry points, +parameter authority, resource ownership, trust requirements, and readiness +evidence. Do not claim C2a or privacyIDEA-owned token lifecycle. + +Published `capabilities/playbooks/key-cape.lightweight-sso.yaml` and +`capabilities/playbooks/key-cape.privacyidea-token-authority.yaml`. The latter +claims the KeyCape integration for C2b while leaving factor enrollment, token +state, custody keys, validation decisions, and lifecycle with privacyIDEA. + +## Define bounded service-auth contracts + +```task +id: KEY-WP-0009-T02 +status: done +priority: high +state_hub_task_id: "212eb75c-a36c-5cb8-bf67-7d524f00f9ef" +``` + +Define the KeyCape service-auth claims, renewal/expiry, failure, custody, and +owner boundaries required by secrets-engine and OpenBao JWT roles. Add +per-client access-token lifetimes so a bounded client contract does not depend +on an unrelated global default. + +Added a validated 1m-1h per-client `tokenLifetime` override, applied it to JWT +`exp` and `expires_in`, and documented claims, renewal, expiry, residual JWT +validity, explicit OpenBao cleanup, and no-fallback failure semantics in +`docs/openbao-service-auth-contract.md`. + +## Accept coding-agent issuance ownership + +```task +id: KEY-WP-0009-T03 +status: done +priority: high +state_hub_task_id: "c41e0144-8169-57cf-8c34-eb52e65d6d76" +``` + +Publish the non-secret static registration for `codex-railiance-platform` with +the exact audience, subject, tenant, role, scope, and 15-minute lifetime already +accepted by railiance-platform. KeyCape owns JWT issuance and client disablement; +railiance-platform owns the exact-bound OpenBao role and policy; OpenBao owns +resulting token enforcement; secret values remain outside this repository. + +Accepted ownership through the exact non-secret registration in +`config/service-clients.example.yaml`. Added the parallel reviewed +`secrets-engine-openbao` contract. Live value generation/materialization, +deployment merge, and OpenBao role/policy application remain with their named +custody and platform owners and are not implied by this source registration. + +## Validate and hand off + +```task +id: KEY-WP-0009-T04 +status: done +priority: high +state_hub_task_id: "6945c0c0-bbb6-552b-8d62-d806d3fd0018" +``` + +Run the canonical declaration validator, Go formatting/build/vet/tests, and +repository checks. Reply to NetKingdom, secrets-engine, ops-warden, and +railiance-platform with revision-independent paths and exact ownership status. + +Both declarations pass NetKingdom's canonical validator. The full Go suite, +vet, build, `gofmt`, YAML parsing, and `git diff --check` pass using an explicit +Go 1.23 toolchain and writable local cache; the workstation's default Go cache +is read-only and was not used. + +2026-09-05 correction: the earlier statement that State Hub handoffs were +delivered was not supported by receipt IDs. The audit found no matching handoff +receipts in the Hub response. KEY-WP-0014-T02 records the correction. + +2026-09-08 closure. Verification re-run on `main` with `GOTOOLCHAIN=go1.23.0` and +a writable `GOCACHE`: `make contract-test` PASS, `go build ./...`, `go vet ./...` +and `go test ./...` all pass, `git diff --check` clean. `gofmt` had drifted on +`src/internal/server/telemetry/events.go` and `src/tests/migration/scenario_c_test.go` +(comment alignment only, no semantic change); both were reformatted so the +formatting claim now holds. + +The four handoffs were then actually sent, and each receipt is readable back via +`GET /messages/?from_agent=key-cape`: + +| Recipient | Receipt id | +| --- | --- | +| netkingdom | `692ed0d7-e21b-48e7-8600-5b32b551c627` | +| secrets-engine | `479a1698-87dc-4ee2-a732-c2dc18bc4e84` | +| ops-warden | `5bc58847-eed5-4164-bb0f-a0d8455e75b8` | +| railiance-platform | `9c6a4e70-6bc0-4ad6-8293-35a56e507dfc` | + +Each names revision-independent paths, the 2026-09-08 verification result, and +the same ownership split: KeyCape owns issuance, static registrations, client +disablement and the claim set; secret custody, OpenBao roles/policies and token +enforcement, privacyIDEA factor and token lifecycle, resource authorization and +deployment remain with their named owners. C2a is not claimed. This closes the +notification gap; it is still not evidence of live deployment or provider cutover. + +### 2026-09-27 operator tenant-zero correction + +OpenBao infrastructure identities belong to `tenant:platform`, not Coulomb. +Corrected both static infrastructure registrations, matching source tests and +the tenant/OpenBao contracts. Consumer preflight and platform role sources are +updated together; no live registration or token migration is claimed. The +provider contract remains complete; RPF-WP-0035-T02 and SECRETS-WP-0008-T06 +retain coordinated custody/deployment and native rejection/revocation proof. +Human fallback and workload tenant grants are not elevated to platform. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0010-openbao-operator-loopback-callback.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0010-openbao-operator-loopback-callback.md new file mode 100644 index 00000000..81614eeb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0010-openbao-operator-loopback-callback.md @@ -0,0 +1,33 @@ +--- +id: KEY-WP-0010 +type: workplan +title: "Admit the operator-tunneled OpenBao callback" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-08-23" +updated: "2026-08-23" +depends_on: + - RAILIANCE-WP-0027 +state_hub_workstream_id: "8bdf9a24-9dc1-53f9-9448-3b29c36d9950" +--- + +# KEY-WP-0010 — OpenBao operator loopback callback + +## Admit and validate the exact callback + +```task +id: KEY-WP-0010-T01 +status: done +priority: high +state_hub_task_id: "46d78907-bda4-5cc9-853d-98c0026d9fe1" +``` + +Added the exact operator-only callback +`http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback` to the +source-owned `openbao-admin` static client in `config/dev-config.yaml`. The +registration retains exact-match redirects and does not authorize a login, +change the OpenBao role, broaden a listener, or handle a credential. A config +test pins the exact URI and proves the complete source config validates. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0011-live-secret-exposure-recovery.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0011-live-secret-exposure-recovery.md new file mode 100644 index 00000000..f7b906f5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0011-live-secret-exposure-recovery.md @@ -0,0 +1,68 @@ +--- +id: KEY-WP-0011 +type: workplan +title: "Recover from live KeyCape Secret output exposure" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-08-23" +updated: "2026-08-23" +state_hub_workstream_id: "158aa957-8818-52ea-b7a5-3962cf30ba5e" +--- + +# KEY-WP-0011 — Live KeyCape Secret exposure recovery + +Recover safely after the base64 data map of Kubernetes Secret +`sso/keycape-config` was mistakenly rendered in agent tool output during a +metadata check. Never reproduce, decode, or store the exposed payload. + +## Contain and notify owners + +```task +id: KEY-WP-0011-T01 +status: done +priority: high +state_hub_task_id: "a8f5e33d-c725-5181-849e-a42bba34d3d8" +``` + +Stopped live rollout work, treated the payload as exposed, and notified +railiance-platform and NetKingdom without values. The affected classes are the +KeyCape RS256 signing private key and credentials embedded in `config.yaml` +(LLDAP bind credential, Authelia client credential, and privacyIDEA admin +token). No value was decoded, reused, or intentionally repeated and no live +mutation was attempted. + +## Approve and execute coordinated rotation + +```task +id: KEY-WP-0011-T02 +status: done +priority: high +state_hub_task_id: "9d60569c-a0f1-53a9-b4dc-01687ab92bcc" +``` + +Approved by the operator on 2026-08-23, accepting deliberate invalidation of +active sessions and a brief SSO interruption. The RS256 key, LLDAP bind/admin +credential, Authelia-KeyCape client credential, and privacyIDEA application +signing secret were replaced without rendering values. The exposed +privacyIDEA admin JWT was removed from live use. Replacement material is +recoverable only through the NetKingdom age recipient in the encrypted +recovery overlay under `history/`. + +## Verify recovery without values + +```task +id: KEY-WP-0011-T03 +status: done +priority: high +state_hub_task_id: "a079573d-97fd-5c6c-b1f9-c0af65e2933a" +``` + +Verified changed resource versions and public JWKS fingerprints, four Ready +deployments, successful replacement LLDAP and Authelia authentication, +wrong-secret rejection, explicit privacyIDEA fail-closed denial, issuer +discovery, and the OpenBao callback handoff to Authelia. The temporary LLDAP +force-reset flag was removed. No Kubernetes Secret payload or authentication +value was rendered during recovery. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md new file mode 100644 index 00000000..0f3deb62 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md @@ -0,0 +1,57 @@ +--- +id: KEY-WP-0012 +type: workplan +title: "Repair UserInfo canonical subject resolution" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: userinfo-canonical-subject-resolution +created: "2026-08-31" +updated: "2026-09-01" +state_hub_workstream_id: "2aca83d8-4e80-52c6-8929-f1e957576b47" +--- + +## Repair subject lookup + +```task +id: KEY-WP-0012-T01 +status: done +priority: high +state_hub_task_id: "bc1ee2c7-a321-5914-96d1-9cb871f0ee1b" +``` + +Resolve the canonical LDAP-DN `sub` emitted by the token endpoint without +passing it to the username-only repository lookup. Preserve stable subject +semantics and verify any `preferred_username` lookup against the canonical ID. + +## Regression verification + +```task +id: KEY-WP-0012-T02 +status: done +priority: high +state_hub_task_id: "7073cc30-a518-5c27-acaf-1a323fd981c6" +``` + +Cover canonical-ID, legacy username-sub, missing subject, and suspended-user +behavior. Run the KeyCape test suite and image build checks. + +## Deploy and verify OpenBao OIDC + +```task +id: KEY-WP-0012-T03 +status: done +priority: high +state_hub_task_id: "571c5237-93fa-5c61-87af-974e02fd8929" +``` + +Publish and deploy the corrected KeyCape image, prove `/userinfo` accepts a +fresh human access token, then resume the governed Policy Nexus bootstrap. + +Completed 2026-09-01. Forgejo Actions run 14 built commit +`153258b9d39273d53ca7ab5d1bdc09961ea9026e`; image digest +`sha256:4c7193efc3e50bd72a01812a583e823cc04f41ca96ef387b31ba9ae2533b80d0` +rolled out successfully. A fresh OpenBao OIDC flow passed `/userinfo`, and the +governed Policy Nexus bootstrap subsequently completed with deterministic +session revocation. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0013-approval-engine-resource-audience.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0013-approval-engine-resource-audience.md new file mode 100644 index 00000000..7d77aee3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0013-approval-engine-resource-audience.md @@ -0,0 +1,573 @@ +--- +id: KEY-WP-0013 +type: workplan +title: "Approval-engine resource audience and client registrations" +domain: infotech +repo: key-cape +status: finished +flavor: implementation +owner: codex +topic_slug: approval-engine-resource-audience +created: "2026-09-05" +updated: "2026-09-30" +state_hub_workstream_id: "6e815d88-b0e3-5ce0-be5d-13ab15917f7f" +--- + +Source: approval-engine inbox request 5583e896-52f2-45bd-895f-02f227b7e836, +reviewed against its local registration and caller-authentication contracts. + +## Implement static resource audiences + +```task +id: KEY-WP-0013-T01 +status: done +priority: high +state_hub_task_id: "a92432a2-4e92-5b90-be2b-d82b784bf8f0" +``` + +Added optional static audience configuration for access tokens in both grants; +ID tokens retain the relying-party audience. Added human access-token scope. +Published bounded approval service fragments and the human registration contract. +Regression tests cover the default audience, request override resistance, JWKS +signature validation, ID-token separation and service registration scope isolation. +Browser requests and token exchanges now enforce the client scope allow-list, +including grants removed after authorization. + +## Provision and prove live registrations + +```task +id: KEY-WP-0013-T02 +status: done +priority: high +state_hub_task_id: "607897c5-bad9-55e5-86df-7802f592d6e8" +``` + +Needs deployment-owned custody for both new service secret references and the +compatible image/configuration rollout. The upstream issuer is now verified and +pinned by NetKingdom (2026-09-09). Deploy the implementation and service registrations together, +then prove live JWKS verification and denied excess scopes without logging values. +Local signature proof is not live rollout evidence. See docs/approval-engine-auth-contract.md. +The separate human UI callback gate is retained in T05; a bearer-only resource +server does not own a redirect and its absence does not block these service clients. + + +2026-09-05 follow-up: read-only deployment metadata shows the current image is +forgejo.coulomb.social/coulomb/key-cape:main-153258b and only the Qonto service +secret environment reference is present. The two approval clients are not +materialized through deployment environment references. Published a concrete +non-secret admission packet at docs/approval-engine-provisioning-request.yaml. +Custody routing has no exact admitted lane for these two clients. `warden plan` +returned founder_required but matched an unrelated generic database lane; that +mismatch is not authority to provision. Human callback clarification is pending. +No secrets were read or production resources changed. + +2026-09-08: the two blocking requests were sent and their receipts are readable +back via `GET /messages/?from_agent=key-cape`. + +- `278a3ebe-b529-49f6-bd1a-e3ebcf318260` -> railiance-platform. Custody admission + for both clients, quoting `docs/approval-engine-provisioning-request.yaml` + verbatim: exact OpenBao paths and `client_secret` field, the two Kubernetes + delivery references, the two `KEYCAPE_*_CLIENT_SECRET` environment names, + `tenant:platform` per decision `5ed3fb35-eca9-413a-82b9-95171ba85bf6`, and the + 15m lifetime. It asks for four things explicitly: confirm/correct the paths and + fields, confirm the delivery references, name the authority for the attended + first provision, and agree a rollout window in which registrations and the + KeyCape version that reads them deploy together. It restates that the + `warden plan` `founder_required` result matched an unrelated generic database + lane and is not being treated as authorization. +- `43fbd997-6e22-4859-8433-58b784c2df6c` -> approval-engine. Requests the two + missing human-registration inputs — the exact `client_id` and the full callback + URI — noting redirects are matched exactly and a near-miss fails closed at + `/authorize`. Restates the human client shape (public, S256 PKCE, + `audience: approval-engine`, `allowedScopes: [openid, approval:approve]`, + `mfaRequired: true`, no consume grant) and asks approval-engine to confirm it + validates the access token, not the ID token, against the deployed `/jwks`. + +Deployment state is unchanged from the 2026-09-05 observation: neither approval +client is materialized. Task remains `wait` — the two admissions are owned by +railiance-platform and approval-engine respectively, and live provisioning proof +cannot begin until both land. Still no secret read and no production resource +changed. + +2026-09-08, second pass. Custody and the human callback are still owed, but one +part of this task was ours all along and was not built: the task requires proving +"live JWKS verification and denied excess scopes without logging values", and +there was no way to do that except by hand, against production, at the moment +custody lands — which is the worst time to be improvising a check. + +Shipped `keycape verify-client` (`src/internal/authclient/verify.go`, documented +in `docs/native-authentication.md`). Per registration it verifies discovery +origin, the `client_credentials` exchange and its RS256 signature against the +deployed JWKS, exact `sub`/`tenant`/`roles`/`principal_type`, that every +`-deny-scope` is refused, and that the token carries no scope that was not +requested — the last being a gap the caller commands do not cover, since they +prove requested scopes were granted and not that nothing extra came back. +Failures name the claim, never the observed value, and nothing is written to +disk, so it is safe to run against production. The exact invocation for each of +the two clients is now recorded in the `verification:` block of +`docs/approval-engine-provisioning-request.yaml`, so admission hands back a +command rather than a description. + +Tests: `src/internal/authclient/verify_test.go` covers the passing case, an +over-broad registration, a live predecessor secret, an identical "rotation" and +output non-disclosure. Verified with teeth — neutering `mustFail` makes the suite +fail rather than pass silently. + +Task stays `wait`. What is owed from elsewhere is unchanged and unreduced: the +two secret values through an admitted custody path, and the exact human +`client_id` and callback URI. Nothing here provisions anything. + +### 2026-09-08 attended upstream issuer proof — T02 return + +The user explicitly admitted the prepared temporary probe. At 21:44:44 UTC its +actual Authelia authorization-code exchange verified the signed issuer +**`https://auth.coulomb.social`**, including signature, keycape audience, validity +window and browser-bound nonce. The pinned Job exited 0. No token or user claim +was retained, and no downstream credential was issued. + +All five temporary resources and the Pod were removed with scoped cleanup; +KeyCape Deployment and config Secret metadata remained unchanged. Receipt: +`docs/evidence/2026-09-08-upstream-issuer-live-proof.json`. + +This closes the actual-token issuer observation gate. T02 remains wait for the +configuration owner to ensure `authelia.issuer` is pinned to the verified HTTPS +value, named CCR-2026-0017/0018 reviews, custody delivery and compatible rollout. +The diagnostic did not prove downstream KeyCape MFA/application login and did +not activate custody. Do not repeat discovery or request another observation +unless the provider/configuration or proof context changes. + + +2026-09-09 configuration-owner return: NetKingdom's exercised, revision-guarded +issuer-only helper inserted `authelia.issuer: https://auth.coulomb.social` into +`sso/keycape-config`; resourceVersion `51346058` -> `58713343`. Independent +readback matches. Every other configuration byte and Secret data entry, including +the private key, was preserved. Thirteen tests passed. Receipt: +`docs/evidence/2026-09-09-upstream-issuer-pin.json`. + +The stored issuer configuration prerequisite is closed. No process restart, +image rollout, custody activation or downstream login proof occurred. T02 stays +wait for the named CCR reviews, attended custody and compatible rollout. The +single-instance replacement and existing-human-login checks remain mandatory. + +2026-09-09 completion: both named CCR reviews are recorded. Verifier-side custody, +ESO delivery and the compatible pinned image/config are now live. Both service +clients passed the pinned verifier plus independent live signature and exact +claim/lifetime checks; wrong-secret and excess-scope requests were rejected. +The existing OpenBao human login passed before and after replacement; each +attended session self-revoked. Receipt: `docs/evidence/2026-09-09-keycape-verifier-admission.json`. + +The deployment owner exercised compatible rollback and version-preserving resume. +The final helper checks containerd imageID against the pinned manifest and uses +the existing 30-second future-iat bound without extending token expiry. The +pinned native verifier runs in the contained owner process because workload +network policy excludes public issuer HTTPS. No network grant was broadened. + +Live residual ownership remains KEY-WP-0013-T05 for the approval UI callback/MFA; +RPF-WP-0035-T05 for separate client-side delivery; AUDIT-WP-0009-T09 for audit +custody; APPROVAL-WP-0002 and SECRETS-WP-0009-T03 for end-to-end claim/consume, +expiry/revocation and native delivery. Natural JWT expiry and real predecessor +rotation were not exercised by initial provisioning. + +## Reconcile tenant vocabularies across approval layers + +```task +id: KEY-WP-0013-T03 +status: done +priority: high +state_hub_task_id: "e98da2eb-1f05-5c7f-bc84-711fa38b8c4a" +``` + +Source: glas-harness inbox message 356f6977-d361-4e3b-83ab-b2c7f4759286 +(GLAS-WP-0015), which asks for the exact store tenant, JWT tenant, CheckRequest +tenant, any permitted mapping and wrong-tenant denial evidence. + +Published `docs/tenant-claim-contract.md`. The KeyCape-owned JWT tenant for all +four reviewed service registrations is `tenant:coulomb`, bound at registration +and required by config validation. Approval store `platform` and policy +`tenant:platform` are owned by approval-engine and flex-auth; KeyCape performs no +normalization or aliasing, so exact comparison does not match today. No mapping +was invented and no live registration or policy subject was changed — the two +admissible resolutions are recorded for the owning parties to decide. + +Added `src/internal/server/oidc/tenant_test.go`: request-supplied `tenant` and +`tenant_hint` cannot alter the claim; two registrations never carry each other's +tenant (the wrong-tenant denial basis); human tokens default to `tenant:coulomb` +rather than an empty claim. Local issuance proof only, not live-rollout evidence. + +## Align approval registrations to tenant:platform + +```task +id: KEY-WP-0013-T04 +status: done +priority: high +state_hub_task_id: "d93e21cc-a422-5e78-981f-9c00f3161375" +``` + +Source: glas-harness inbox message f487c63a-dd7e-4ad8-9188-eec9fbea59c6, +operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 ("Use tenant:platform for +the Glas approval dependency chain", verified resolved in the hub). This closed +resolution 2 of the two options recorded in KEY-WP-0013-T03. + +Changed the `tenant` field on exactly `secrets-engine-approval` and +`approval-engine-operator` in `config/service-clients.example.yaml` and the two +matching entries in `docs/approval-engine-provisioning-request.yaml` to +`tenant:platform`, each annotated with the decision id. `codex-railiance-platform`, +`secrets-engine-openbao` and the human directory default keep `tenant:coulomb`. +No audience, scope, subject, role, lifetime or MFA change; no cross-tenant grant. + +Evidence: `TestApprovalClientIssuesExactPlatformTenantAndRejectsAliases` (exact +`tenant:platform` with `aud=approval-engine` even when the caller requests +`platform`, `tenant:coulomb` or `TENANT:PLATFORM`), +`TestUnrelatedServiceClientKeepsCoulombTenant`, and +`TestServiceRegistrationTenantsAreExactPerDecision`, which pins every reviewed +client's tenant against the real fixture so a reintroduced alias fails the build. +Full `go test ./...` and `go vet ./...` pass. Choice only — live provisioning and +the other admission gates remain with KEY-WP-0013-T02. + +2026-09-09 observation, read-only. Custody has been admitted since the +2026-09-08 record and the deployment now carries the pieces T02 was waiting for: +`sso/keycape-config` registers `secrets-engine-approval` and +`approval-engine-operator` alongside the existing clients; the secrets +`keycape-secrets-engine-approval-client` and +`keycape-approval-engine-operator-client` exist in the namespace; both +`KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET` and +`KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET` are wired into the pod; and the +image is pinned by digest to +`sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611`, the +candidate T06 published. + +What that does and does not establish. The live issuer passes every conformance +check that needs no credential — it advertises itself, offers the profile +authorization surface, publishes usable RS256 keys and advertises no excluded +grant (`src/tests/conformance`, run against `https://kc.coulomb.social`). It does +**not** establish the claim contract: subject, tenant, roles, scopes, lifetime +and excess-scope denial per client are exactly what `keycape verify-client` +exists to prove, and that needs the client secret in the environment. + +Deliberately not done here. Reading either secret to run the verifier is +client-side retrieval, which the provisioning packet records as **not admitted** +(`custody_return.scope`: verifier-side copies only). So the remaining proof is an +attended operator action, not something to take by reading the secret. T02 stays +`wait` on that run, not on custody. + +**Correction, same day.** The refusal above was right; the conclusion drawn from +it was not. The attended run had already happened, on 2026-09-09T00:10Z, and its +receipt is committed in this repository at +`docs/evidence/2026-09-09-keycape-verifier-admission.json`. Per client — both +`secrets-engine-approval` and `approval-engine-operator` — +`live_jwks_signature_verified: true`, `exact_claims_verified: true`, +`excess_scope_denied: true`, `wrong_secret_denied: true`, +`lifetime_seconds: 900`, `maximum_future_iat_seconds: 30`, +`pinned_artifact_verifier_passed: true`, `verifier_location: "attended owner +process"`. `credential_values_emitted: false` and +`client_side_read_admitted: false` throughout, so it was run the admitted way. +The claim contract is established and T02's `status: done` is correct; it does +not revert to `wait`. + +The reasoning error is worth naming because it is the third of its kind here in +two days: "not admitted **for me**" was read as "not done **by anyone**", without +checking `docs/evidence/`. The preceding paragraph's read-only observations are +sound and its restraint about not reading the secret is exactly right — only the +final sentence was wrong, and it is retained above rather than deleted so the +correction has something to point at. + +What the receipt itself declines to claim, which is the honest residual and +matches this task's earlier record: `real_predecessor_rotation_tested: false`, +`observed_wall_clock_expiry: false`, and under `limits`, +`actual_predecessor_rotation_observed: false` and +`wall_clock_jwt_expiry_observed: false`. Natural expiry and a real predecessor +rotation were not exercised by initial provisioning and are not claimed by +anyone. Predecessor rejection is implemented and unit-tested in +`keycape verify-client`; it has not been run against a real rotation, which is +KEY-WP-0014-T04's territory and still has no admitted execution authority. + +**Independently confirmed by the custody owner, 2026-09-09** (railiance-platform +`ae6ff4bc`, `570e6746`). They reached the same reading from their own side and +add one datum this record did not carry: `human_client_consume_denied: true` for +both clients, alongside the signature, exact-claim, lifetime, excess-scope and +wrong-secret results. They confirm the verifier ran twice — once during +activation and again after rollout — at deployment generation 38, single ready +replica, signing key unchanged, no credential values emitted, and state that T02 +can be closed against that receipt rather than against a run KeyCape cannot make. + +They also correct one thing in our framing, and they are right: the packet says +*client-side retrieval* is unadmitted, which remains true, but the attended +operator path is not a client-side read and never required one. The two are +distinct and this task had been treating them as the same constraint. + +**Do not cite this receipt as rotation evidence.** Both owners now state the same +two gaps independently: `real_predecessor_rotation_tested: false` and +`observed_wall_clock_expiry: false`. Neither is a T02 acceptance criterion, and +neither is closed by anything recorded here — see KEY-WP-0014-T04. + +**Client-side retrieval is prepared but still unadmitted.** CCR-2026-0019 +(secrets-engine reader) and CCR-2026-0020 (approval-engine-operator reader) are +`in_flight` under RPF-WP-0035-T06. 0020 has no named presenting actor in any +owner source, and railiance-platform left it undetermined rather than guessing — +correctly. KeyCape's view, asked for as the issuer of the identity and recorded +in `docs/approval-engine-provisioning-request.yaml`, is that the registration as +issued carries both `approval:create` and `approval:approve`, so a single +presenter can create and then approve the same entry. That is a +separation-of-duties question about who may hold the credential, not a defect in +the token: KeyCape issues the grants approval-engine requested. The decision is +approval-engine's and the doctrine question is gate-house's. + +**Answered 2026-09-09, and the answer withdrew the client.** approval-engine +checked and the presenter is nobody (`056aef0d`). Nothing in their repository +obtains an OAuth token — the engine only verifies them, there is no +client-credentials grant in the package, and the one outbound credential is a +mounted audit-token, not an OAuth client. Scope by scope the bundle never +described a single actor: `approval:approve` now belongs to the informed-decision +human client, `approval:emit` is redundant because the running server emits its +own heartbeat with no token, and no requester identity was ever settled for +`approval:create`. It was a convenience bundle written when a lifecycle operator +was assumed to exist. + +They asked railiance-platform to cancel the client-side reader CCR-2026-0020 as +an owner decision. Verifier custody CCR-2026-0018 stands, so the client remains +authenticable if a presenter appears, and they asked for it to be kept on that +basis rather than removed. Annotated as withdrawn in +`config/service-clients.example.yaml` and the provisioning packet; not deleted. +The separation-of-duties observation above is retained because it constrains the +re-request: when a presenter is named the registration comes back **narrowed**, +and `approval:approve` must not travel with the operational scopes. + +**One thing "do not provision" cannot undo, raised rather than filed.** This +client is already live — provisioned in the 2026-09-09 attended rollout and +verified in `docs/evidence/2026-09-09-keycape-verifier-admission.json`. So the +estate now holds a confidential credential that can create *and* approve +approvals, that nobody presents, and that no consumer is waiting on. That is +standing capability with no counterparty, which is a worse resting state than +either provisioning it for a named presenter or disabling it. KeyCape owns client +disablement, so the action is ours to take and not ours to decide alone; raised +with approval-engine and railiance-platform rather than exercised, since removing +a live registration is a deployment change. Recorded here so it is not lost: +**an unpresented create+approve credential should not be left standing +indefinitely.** + +## Assign and register the human approver browser client + +```task +id: KEY-WP-0013-T05 +status: done +priority: high +assignee: the-custodian +state_hub_task_id: "9a782909-91db-59fa-aae7-83766f4fbb0d" +``` + +Own the residual human registration separately from service-client T02. Reuse +an existing accepted UI if available; do not invent a callback on approval-engine. +Require public S256 PKCE, exact redirect, MFA, approval:approve without consume, +and a real human access-token proof. No service credential substitutes for this. +HFACT-WP-0001-T03 consumes the acceptance where a human approval is required. + +2026-09-09. The owner exists: `informed-decision` (hub repo cf4c7da8) claims the +approver surface and owns the two strings. approval-engine was right that a +bearer-only resource server never owned them. Both wrote in asking to be told +early about problems with the registration shape rather than at handover, so +their requested shape was checked against the source rather than agreed on +paper. Three results. + +**Scope gap — accepted, theirs was right.** `[openid, approval:approve]` cannot +render a decision: `GET /v1/approvals/{id}` and `/claim` both need +`approval:read`, so the surface could submit an entry it was never able to +display. Published `[openid, approval:read, approval:approve]` in +`docs/approval-engine-auth-contract.md` and the provisioning packet. Reading +through a service identity was the alternative and is worse: it weakens the +evidence-of-what-this-person-saw claim the component exists to make. +`approval:consume` stays excluded. + +**Assurance shape — published, and a defect fixed.** Both asked for a documented +shape; KeyCape already emitted one, so it is written down rather than +renegotiated: `level` (aal1|aal2, ~acr), `methods` (~amr), `mfa`, `source`, `at`. +Writing it down surfaced that `at` was the token **mint** time, not the +authentication time. Those differ by hours whenever a browser session is reused, +and approval-engine persists this object verbatim as the only downstream record +that MFA happened — so a stored approval could have evidenced MFA at a moment the +person proved nothing. `PKCESession.AuthTime` now carries the original login +instant through session reuse, with mint time as the fallback for sessions +predating the field. Covered by `src/internal/server/oidc/assurance_time_test.go`. + +**Blocker found before anyone built on it — a human token cannot carry +`tenant:platform`.** `effectiveTenant` resolves the human tenant from the +directory user, not the client registration, and no adapter populates +`domain.User.Tenant`, so every human token defaults to `tenant:coulomb`. The +per-client `tenant` field that carries `tenant:platform` on the two service +clients is read only on the `client_credentials` path. approval-engine compares +tenant by exact string equality and pins near-miss spellings as refused, so an +approver token issued today would be rejected — and it would present as a failed +approval, not as a registration defect. + +Two resolutions, sent to both owners (`67132ddc`, `a6a070a1`, pointer +`42d435e8`) rather than chosen here, because the choice decides what a human's +tenant *means*: (1) directory-sourced, approvers carry `tenant:platform` on their +LLDAP record — keeps tenant a property of the person, needs a directory attribute +and an owner, and changes those users everywhere; (2) registration-bound and +fail-closed, symmetric with the service clients and with decision +`5ed3fb35-eca9-413a-82b9-95171ba85bf6` — defensible only because registrations +are static and deployment-owned, since dynamic registration is excluded by +design. KeyCape leans to (2) and deliberately implemented neither: (1) is a +directory-ownership question and (2) writes a cross-tenant capability into the +issuer. + +**Tenant blocker resolved in code, 2026-09-09.** On review the choice did not +have to be made to unblock: the two resolutions differ in where a human's tenant +*comes from*, and an implementation exists that is correct under either. A client +registration may now declare a `tenant`, and `humanTenant()` resolves it by four +rules — no declaration keeps the directory answer unchanged; a declared zone +applies when the directory has placed the user nowhere; agreement passes; and a +declared zone that **conflicts** with a directory assignment refuses issuance +(`403`, `error_type: tenant_binding`) rather than relabelling the user. + +That refusal is the whole design. A registration can bind a zone for unplaced +users and can never move a placed one, so option (2) is available now without +writing a general cross-tenant override into the issuer, and if the owners prefer +option (1) the same code stops supplying the zone and starts enforcing agreement +with it — no second migration and no window where a stale registration silently +wins. It is safe only because registrations are static and deployment-owned; +`docs/tenant-claim-contract.md` records that this rule must be revisited if +dynamic client registration is ever admitted. + +Evidence: `src/internal/server/oidc/human_tenant_test.go`, four cases including +the relabel refusal. Verified with teeth — neutering the conflict check makes +`TestHumanTenantRefusesToRelabelAPlacedUser` fail rather than pass silently. +`docs/tenant-claim-contract.md` carries the resolution table, +`docs/approval-engine-auth-contract.md` states the requirement on the approver +client, and the provisioning packet now declares `tenant: tenant:platform`. + +**Cleared and registered 2026-09-10.** informed-decision submitted the two +strings with the origin already live: + + client_id informed-decision-approver + redirect_uri https://decisions.coulomb.social/auth/callback + +The host is `decisions.coulomb.social`, not the `decide.coulomb.social` of an +earlier draft. They verified the origin rather than reporting it: `/` and +`/auth/callback` both return 200 from 92.205.62.239 on a Let's Encrypt +certificate CN=decisions.coulomb.social valid 2026-09-10 to 2026-12-09. The path +serves a placeholder while their surface is gated on APPROVAL-WP-0002-T01, which +does not affect the registration — the redirect is matched as an exact string at +`/authorize` and never fetched. + +Published in `config/service-clients.example.yaml`: public `authorization_code` +with S256 PKCE, `audience: approval-engine`, scopes +`[openid, approval:read, approval:approve]`, `mfaRequired: true`, declared +`tenant: tenant:platform`. No `secretRef` — PKCE is the whole proof — and no +`serviceSubject` or `roles`, which validation rejects on a browser client. + +`TestApproverRegistrationShapeIsExact` pins every field, so widening a scope or +relaxing MFA fails the build rather than reading as an edit, and asserts the +registration passes startup validation — which proves the KEY-WP-0028 tenant +exemption holds for the registration that depends on it, not only in a synthetic +case. + +Two existing guards fired on the way in, both correctly, and neither was loosened: + +- `TestServiceRegistrationTenantsAreExactPerDecision` refused an unreviewed client + carrying a tenant, which is exactly its purpose. The approver was added to its + reviewed set as a deliberate act. +- `TestServiceRegistrationAudienceAndScopeIsolation` panicked on + `secretRef[4:]`, an assumption that was safe while the fixture held only + confidential clients. The approver is the first public one; the loop now guards + on the `env:` prefix. + +The tenant is declared, and the token says so: `tenant_source` is `registration`, +never `directory`. Per gate-house's correction that is adequate for +approval-engine's store-isolation gate and **never** for doctrine turning on this +person's membership of the zone. + +Not live: this is a published registration fragment. Deployment is owner-side and +nothing has been issued to a real approver. + +## Make negative rollout evidence discriminate actual issuer refusal + +```task +id: KEY-WP-0013-T06 +status: done +priority: high +assignee: the-custodian +state_hub_task_id: "65132e1d-10db-5915-a664-83beaf30365a" +``` + +Critical-path review found `mustFail` accepted any Exchange error: timeout, +HTTP 5xx, invalid token or JWKS failure could falsely prove excess-scope denial +or predecessor rejection after a successful positive exchange. Preserve a bounded +typed provider refusal, require the exact token endpoint/status/error/feature, +and prove the checker rejects unrelated failures without exposing provider bodies. +Publish the verified image candidate and return its immutable digest to T02. + +Platform's 2026-09-08 return is RPF-WP-0035-T05 and proposed CCR-2026-0017/0018. +The custody field is `CLIENT_SECRET`; Kubernetes key `client-secret` and both env +names remain as requested. These records cover verifier-side delivery only. +Owner approval, client-side retrieval and the actual upstream ID-token issuer +proof remain distinct gates. Public discovery currently advertises +`https://auth.coulomb.social`; that alone is not the signed-token observation. + +T06 completion: full Go suite and vet passed; published code `dcebd46` and pulled the image by digest `sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611`. Container `keycape verify-client --help` confirms the command is present. `docs/approval-clients-rollout.md` and its proposed deployment patch record configuration/issuer, first-provision, single-instance rollout, acceptance and rollback. This closes verifier preparation only; T02 and T05 retain the explicit live dependencies. + + +## Prepare a bounded upstream issuer observation before client rollout + +```task +id: KEY-WP-0013-T07 +status: done +priority: high +assignee: the-custodian +state_hub_task_id: "62bc6d8e-dbb2-57b6-a30b-472ffd06b497" +``` + +2026-09-08: T02's actual upstream signed-token issuer gate had no executable +observation path. A downstream OpenBao/KeyCape login cannot return the upstream +Authelia token; it remains inside the adapter. Prepare `probe-upstream-issuer` +and an exact-state temporary route so one attended flow can be verified before +changing the normal KeyCape Deployment or its configuration. + +Acceptance: signed issuer/audience/time/nonce proof without token or user-claim +output; browser/state binding and one-shot refusal; finite issuer allowlist; +ten-minute bound; no downstream credential; current Traefik route isolation; +immutable image; reviewed temporary Job/config projection, network policies and +owner-reference cleanup. Normal callbacks and the production issuer stay on +the existing Deployment. The probe mounts only `config.yaml`, not `key.pem`, +and has no Kubernetes API token. That file contains existing credential data; +its use by the temporary diagnostic needs deployment-owner admission. + +Source tests, image and deployment packet close this preparation task. The +attended live receipt and issuer pin remain in T02, alongside the named CCR +reviews and custody/rollout acceptance. Preparing the diagnostic does not +approve CCR-2026-0017/0018 or close any live factory gate. + +T07 preparation complete: source `6f33abd`, immutable image +`sha256:0c85ed377cae7ae6ca5b5c56b1a52930e706b3cb78009747e42f551e869a22e4`. +Complete Go race suite and vet passed; five renderer/collector checks, seven +real Traefik 3.6.7 route cases and Railiance server dry-run passed. The packaged +command ran as UID 65534 with no network and a read-only filesystem and produced +the expected bounded idle-timeout receipt. See `docs/upstream-issuer-proof.md` +and `docs/evidence/upstream-issuer-probe.json`. T02 retains the live proof and +configuration-owner admission; no production resource or custody value changed. + +## Status 2026-09-30 (CUST-WP-0074) + +All tasks T01-T07 are `done`; nothing in this plan is waiting, so it is +`finished` rather than `blocked`. Residual items live in other plans +(RPF-WP-0035-T05/T06, AUDIT-WP-0009-T09, KEY-WP-0014-T04). The one open +question recorded above, disabling the unpresented create+approve client, is a +KeyCape deployment decision to be tracked in a new plan if taken up. + +**2026-10-10:** KeyCape proposed the joint disablement sequence that +railiance-platform asked for on 2026-09-15, to railiance-platform, approval-engine +and net-kingdom: remove the registration (attended), verify `invalid_client`, +unbind the env, retire the ESO delivery, then retire custody. The proposal executes +nothing. Each owner confirms their step and the founder schedules the attended window. + +**2026-10-10, amended by the owners.** net-kingdom: step 1 (`keycape-client.py +apply --operation remove`, case acceptance CCR-2026-0018) only patches +`sso/keycape-config` and does not restart. The step-3 env removal triggers the one +rollout, and the new pod starts with neither the client nor the env. Step 2's +`invalid_client` check follows that rollout. Rollback runs in reverse: env first, +then the client. railiance-platform (RPF-WP-0035-T10): step 4 also retires the +ClusterSecretStore, the Kubernetes-auth role and the reader policies, through an +attended prune sync run strictly after step 3. Step 5 is a metadata delete, not an +archive, run only after the step-2 receipt. Steps 1-5 can share one attended +window. Still open: approval-engine's reconfirmation that there is no presenter, +and the founder scheduling the window. Before that rollout, run +`keycape check-config` on the patched configuration. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0014-native-credential-lane-handoff.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0014-native-credential-lane-handoff.md new file mode 100644 index 00000000..bacf04c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0014-native-credential-lane-handoff.md @@ -0,0 +1,362 @@ +--- +id: KEY-WP-0014 +type: workplan +title: "Review native login and client credential lane handoffs" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: native-credential-lane-handoff +created: "2026-09-05" +updated: "2026-09-10" +state_hub_workstream_id: "0d003df3-f7d3-5063-8ca0-e1e33f7df74a" +--- + +Source: ops-warden inbox message 0dd9c7bd-0ecd-42d1-806f-7cc4ba9730ed. +The native service exchange and public PKCE login commands are now implemented. +Cross-owner rotation and consumer-specific route cutover remain outstanding. + +## Design owner command and custody boundaries + +```task +id: KEY-WP-0014-T01 +status: done +priority: medium +state_hub_task_id: "0c0a0b61-c19e-5631-9cda-8b2dc0f47d8f" +``` + +Review ops-warden's existing key-cape-oidc-login proxy and +rapp-qonto-keycape-client route contracts. Specify the native interactive login +and bounded exchange commands, token delivery, renewal and custody-mediated +rotation before implementation. Keep secret custody with OpenBao and avoid +retiring the proxy until replacement commands have equivalent verification. + +## Verify handoff delivery evidence + +```task +id: KEY-WP-0014-T02 +status: done +priority: low +state_hub_task_id: "d9a5de97-b7d5-5599-98c2-eaab32f51495" +``` + +Ops-warden reports KEY-WP-0009-T04's claimed reply did not arrive. Verify prior +receipts for all four named recipients before claiming successful notification. +No outbound coordination messages were sent during the 2026-09-05 repo review. + + +## Implement and verify native caller commands + +```task +id: KEY-WP-0014-T03 +status: done +priority: high +state_hub_task_id: "4e46f474-a15e-594f-ae07-ad37a9667d89" +``` + +Implemented keycape service-token and login with HTTPS discovery, RS256/JWKS +verification, exact audience bindings, PKCE/state/nonce, private out-of-Git file +delivery and redacted errors. Service Basic authentication now form-decodes +credentials. Integration tests exercise the real token/JWKS handlers and browser +callback, including forged state, tampering, excess scope and unsafe output. +See docs/native-authentication.md for invocation and ownership boundaries. + + +2026-09-10. The authority question is answered and the blocker is narrowed to a +schedulable act. railiance-platform named the executor (platform operator, +attended founder session; explicitly not ops-warden, not secrets-engine +autonomously, and no unattended agent), the transport (the governed +`openbao-platform-admin-login` lane via `warden access ... --exec`), and the +authority (founder_required attended OIDC, role=platform-admin). What is missing +is a rotation CCR, which they will prepare *if KeyCape asks* — a two-custodian +CAS rotation with a service restart being a distinct version-guarded operation +rather than an implementation detail of an existing lane. + +**Not asked, deliberately.** Requesting a production credential rotation with a +restart window is the operator's decision, not this repository's, so it was put +to him rather than answered here (ops-warden msg 10fcc8af). + +Established before recommending anything either way: **this rotation has no +incident driver.** KEY-WP-0011 recovered a real exposure on 2026-08-23, and the +coordinated rotation that followed replaced the RS256 signing key, the LLDAP bind +credential, the Authelia client credential and the privacyIDEA admin token. The +`rapp-qonto` client secret was not in that payload — it is an `env:` secretRef +resolved from a separate Kubernetes Secret, never inline in `config.yaml`, which +config validation enforces. So this is lifecycle hygiene, not remediation, and +nobody should be carrying it as leftover incident work. Hygiene can wait for a +chosen window; remediation could not. + +Also recorded: `warden route`/`warden plan` previously returned a generic match +that read as authorization, and offered read transports against a write need. +This repository declined to act on it; railiance-platform independently called it +a defect; fixed under WARDEN-WP-0038. + +## Admit rotation and verify consumer handoff + +```task +id: KEY-WP-0014-T04 +status: done +priority: high +state_hub_task_id: "e7632c71-9676-5837-a925-7f905e3015c5" +``` + +Qonto rotation requires admitted custody/provider transaction and rollback +semantics; see the exact review sequence in docs/native-authentication.md. +The current login proxy yields an OpenBao token, so it cannot be replaced by a +JWT-writing command without adapting its consumer contract. Verify that split +with the platform owner and ops-warden before route changes. No route was changed. + +2026-09-05 receipt audit: GET /messages/?from_agent=key-cape returned one +2026-03 image-publication message and no KEY-WP-0009 handoff receipts for +NetKingdom, secrets-engine, ops-warden or railiance-platform. Corrected the +unsupported statement in KEY-WP-0009-T04. This proves only that no receipts +are present in the current Hub response, not that no message ever existed. +The 2026-09-05 audit's finding stands as recorded; the replacement outbound +messages were sent on 2026-09-08 (below). + +2026-09-08: `0a6c9d88-cf50-45d8-acdc-99a40747683e` -> ops-warden, readable back +via `GET /messages/?from_agent=key-cape`. It puts both open questions to the +route and custody owner in one place: + +1. The `key-cape-oidc-login` proxy yields an OpenBao token and `keycape login` + yields an issuer JWT — different credential types, different verifiers, + different enforcement owners, so not interchangeable. Two admissible options + are offered: keep the proxy and run `keycape login` alongside it for JWT + consumers, or adapt the route's consumer contract to a JWT and then cut over. + Option (a) is recommended until every consumer of the route is enumerated. +2. Qonto rotation for `rapp-qonto-keycape-client`, quoting the five-step reviewed + sequence from `docs/native-authentication.md` and asking who executes the + successor generation and CAS update, in which transport, under what authority. + It records that `warden route`/`warden plan` returned no exact admitted lane + and that a generic match is not being treated as authorization. + +The KEY-WP-0009 handoff ops-warden reported missing has also now been delivered +(`5bc58847-eed5-4164-bb0f-a0d8455e75b8`), closing the KEY-WP-0014-T02 correction. + +Task remains `wait` — no route was changed and no rotation command ships until +ops-warden answers (1) and an execution/rollback authority is admitted for (2). + +2026-09-08, second pass — read ops-warden's own catalog rather than waiting for a +reply, and it changes the shape of both questions. + +`registry/routing/catalog.yaml`, lane `key-cape-oidc-login`: `intended_owner` was +already corrected to key-cape, and `verified: asked-and-waiting` since +2026-08-28. **ops-warden is waiting on us, not the other way round.** The lane is +also `warden_executes: false` with a `wiki_ref` into `CredentialRouting.md` — a +pointer, per their ADR-0001, not a code path with programmatic consumers. So the +consumer-contract risk behind question (1) is smaller than assumed: adding +`keycape login` alongside retires nothing, and the acceptance we owe them is a +statement of ownership, not a cutover. + +Lane `rapp-qonto-keycape-client`: `rotation.owner: key-cape`, +`automatable: true`, and `blocked_on` reads "still no key-cape-native +exchange/rotation command", `verified: source-read` 2026-08-28. **That blocker is +stale.** `keycape service-token` (T03) shipped 2026-09-05 and is exactly the +native `client_secret_basic` exchange they record as absent. It does not clear +the lane, and should be narrowed rather than closed. + +Shipped here: `keycape verify-client`, which is rotation step 4 and the denial +checks step 5 rests on — new exchange verified against deployed JWKS, predecessor +refused, excess scope refused, no value printed or written. See +`docs/native-authentication.md`, "Verifying a live registration". The identical +predecessor case is treated as a failure, because an unchanged secret is a +rotation that did not happen. + +Steps 1-3 remain custody's and are deliberately not automated. Task stays `wait` +on exactly two answers: which option ops-warden takes for the login proxy, and +who executes the successor generation and CAS update under what authority. No +route changed. + +Both catalog corrections were sent, receipts readable via +`GET /messages/?from_agent=key-cape`: + +- `12f1bdfa-b240-414b-aa49-128a4d2acf20` — accepts ownership of lane + `key-cape-oidc-login`, answering the WARDEN-WP-0033-T04 follow-up they have + carried as `asked-and-waiting` since 2026-08-28. States the boundary the lane's + `fetch_command` crosses: key-cape owns browser authentication and the identity + token; the `netkingdom` auth mount, `role=` mapping, token store and + enforcement of the resulting OpenBao token are not ours. Supplies the source + paths they need to move the lane to `verified: source-read`, and records our + revised, lower estimate of the consumer risk — a pointer lane under ADR-0001 + retires nothing when `keycape login` runs alongside it — while asking them to + correct us if the catalog understates its consumers. +- `08d42f47-8e19-4976-98f5-dd259e7b1225` — reports lane + `rapp-qonto-keycape-client`'s blocker as stale against its 2026-08-28 + source-read, since `keycape service-token` is the native `client_secret_basic` + exchange it records as absent and `keycape verify-client` is its rotation step 3 + verbatim. Asks them to **narrow** it to steps 1-2 rather than clear it, with + proposed replacement text, and notes `rotation.automatable: true` is now + half-true and should say so precisely rather than be flipped either way. + +Neither message asks for a route change and neither was one. + +2026-09-08/09 — ops-warden answered both, and the second answer matters more than +the question did. + +**Item 1 answered: option (a).** Keep the proxy as-is and run `keycape login` +alongside it, "for your reason rather than out of caution -- they are different +credential types with different verifiers, so this is not a cutover deferred, it +is two things that were never one." They verified our pointer-layer reading +rather than accepting it, and added a fact we did not have: `warden access` +already excludes `is_login` lanes from raw-value streaming +(WARDEN-WP-0032-T05). Lane `key-cape-oidc-login` moved to `owner-confirmed` with +our boundary written in verbatim (commit c133004). It stays `interim` on purpose: +per their ADR-0003 a lane retires when a front door exists, not when ownership is +agreed, and by our own analysis `keycape login` is not that front door. The entry +now names the retirement condition instead of implying we owe one. + +**Item 2 answered: nobody owns it, admittedly.** Steps 1-2 mutate custody on +platform workload paths, which is railiance-platform's, with `secrets-engine exec` +as the admitted transport. ops-warden declined to take the act on the grounds +that fronting the read lane is not authority to mutate — their ADR-0002 and +ADR-0005 — and routed it themselves rather than describing the route and leaving +it to us. Lane `rapp-qonto-keycape-client` narrowed exactly as asked and moved +`source-read` -> `owner-confirmed`, crediting the correction to us rather than to +a re-read they did not perform. Rotation step 3 now points at +`keycape verify-client` instead of restating its assertions. + +**They found a defect in their own front door because of our caution, and told +us.** Running our need through `warden plan` returns `verdict: autonomous`, +`lane_id: rapp-qonto-keycape-client`, answered with three read transports — a +need containing *generate* and *CAS-write* inherits the verdict of the lane that +reads the same path, because `warden plan` has no read-versus-mutate intent at +all. `autonomous` is documented as the signal to proceed without the founder. +Raised as WARDEN-WP-0038. Their stated conclusion, which we should not soften: "A +counterparty declining to trust our output is not a control, and we are not going +to record it as one." + +Standing instruction from them until WP-0038 lands, recorded here so it is not +lost in an inbox: **treat a `warden plan` verdict on any need containing a write, +rotate or provision act as unreliable.** That applies to this repository whenever +it plans a rotation. + +**On `automatable`, they did not do what we suggested and said so.** We asked +them not to flip the boolean and to state the half-truth precisely; the schema has +one boolean whose only consumer is a future executable driver, and a driver told +`true` would attempt the custody steps. So it is `false`, failing safe, with the +precision moved into per-step notes. They offered to add a per-step field if we +would rather. We would not: a lane-level flag that fails safe plus per-step notes +is the right shape, and asking them to change a schema for our convenience would +be worse than the imprecision. No reply needed on that point. + +T04 stays `wait`: the rotation authority now has a named owner +(railiance-platform, transport `secrets-engine exec`) but does not yet exist, and +nothing here changed a route. + +2026-09-09, guarding against a misreading that is now easy to make. The approval +clients went live and `docs/evidence/2026-09-09-keycape-verifier-admission.json` +records a passing verifier run, so there is now a committed receipt in this repo +that looks like rotation evidence and is not. It records +`real_predecessor_rotation_tested: false` and `observed_wall_clock_expiry: false`, +and under `limits`, `actual_predecessor_rotation_observed: false` and +`wall_clock_jwt_expiry_observed: false`. railiance-platform stated the same two +gaps unprompted from their side (`ae6ff4bc`): "do not cite this receipt as +rotation evidence." + +So `keycape verify-client`'s predecessor rejection has been exercised only +against unit tests and a first provisioning where predecessor and successor were +never distinct. Rotation step 4 is implemented and unproven, which is a different +state from either "missing" or "done", and T04 is where that distinction lives. + +**2026-09-10: the authority is answered. What remains is an artifact and a +decision.** railiance-platform answered the routed question (`7c7228ac`, relayed +by ops-warden `b14655b3`): + +- **Who:** the platform operator, attended, as a founder-attended session. Not + ops-warden, not secrets-engine autonomously, and no unattended agent — + railiance-platform named themselves in that exclusion too. +- **Transport:** the governed `openbao-platform-admin-login` lane, invoked only + through `warden access openbao-platform-admin-login --exec -- ` with a + unique metadata receipt path. The owner command is never run directly. +- **Authority:** `founder_required` attended OIDC via netkingdom + `role=platform-admin`. Nothing else in that repo carries write authority + against `platform/workloads/`. + +**Missing: a rotation CCR.** They will not raise one speculatively, and their +reason is sound — a two-custodian CAS rotation with a service restart is a +distinct version-guarded operation, not an implementation detail of an existing +lane. It must name the CAS precondition and expected version on *both* +custodians, sibling-field preservation on the KV write, the restart window, +bounded predecessor retention for rollback and denial checks, and the +reconcile-to-same-version step on failure. That last is step 5 of the sequence in +`docs/native-authentication.md`, so the reviewed sequence is what the CCR encodes. + +**Blocked on a decision that is not KeyCape's to take unilaterally.** Their offer: +"if key-cape wants the rotation scheduled, say so and I will prepare it under +RPF-WP-0035 with both custodians in one window." ops-warden deliberately did not +ask on our behalf — correctly, per their ADR-0003, since implying an owner request +from a routing errand is quiet ownership. So T04 now waits on the repository owner +deciding whether the Qonto rotation should happen, and when. Nothing indicates +compromise; this is hygiene plus the first real exercise of rotation step 4, which +is why it is a judgement call rather than an obvious yes. + +**Precedent offered, and it is the right one.** The identical two-custodian shape +was exercised 2026-09-09 for the approval clients: attended founder session, +OpenBao KV custody plus a Kubernetes-delivered consumer copy, compatible pinned +rollout, protected recovery retained, and rollback that restored configuration and +image and detached delivery before a clean resume. Their +`scripts/keycape_approval_custody.py` (activate / resume-activate / verify) is the +shape a rapp-qonto rotation should follow rather than a bare `bao kv patch` — for +exactly the provider/consumer consistency reason this task gave when it declined +to ship a wrapper around a raw KV write. They endorsed that refusal unprompted. + +**And the `warden plan` defect is fixed** (WARDEN-WP-0038, 2026-09-10). The Qonto +need now returns `founder_required` naming the write owner, and read transports +are no longer offered against a write need. ops-warden's closing line is worth +keeping: "Your caution was load-bearing and it should not have had to be." + +If the rotation is scheduled, it is also the first opportunity to close the +`real_predecessor_rotation_tested: false` gap recorded above — `verify-client` +with `-previous-secret-env` is exactly step 4, and the predecessor would finally +be genuinely distinct. + +**Decision, 2026-09-10: not yet.** The repository owner declined to schedule the +rotation now. Recorded with the reasoning so it is revisited on evidence rather +than re-litigated from scratch: nothing indicates compromise, the offer stands +open, and the estate already has one founder-attended window pending for the two +fail-closed startup changes. Rotating for hygiene alone, in a second attended +session, against a credential with no incident attached, is work the owner chose +not to spend now. + +This is a deferral, not a refusal, and the thing that should reopen it is a +change in evidence rather than the passage of time: an actual exposure or +suspected one (see KEY-WP-0011), the Qonto secret's age becoming a stated concern, +a consumer requiring proof of rotation, or a decision to prove rotation step 4 +before relying on it elsewhere. Any of those makes the answer different; a +calendar date does not. + +**Closed 2026-09-10.** The paragraph above left T04 `wait` with nothing +outstanding, which is a contradiction: `wait` means waiting or blocked, and this +was neither. Re-reading the task against its own title settles it — *admit +rotation and verify consumer handoff* — and both halves are delivered: + +- **Rotation admitted.** The semantics were reviewed and published + (`docs/native-authentication.md`), the executor and authority are named + (platform operator, attended founder session, governed + `openbao-platform-admin-login` lane, `founder_required` attended OIDC via + `role=platform-admin`), the transport is admitted, and the CCR will be prepared + on request. Step 4 shipped here as `keycape verify-client`, treating an + identical predecessor as a failure because an unchanged secret is a rotation + that did not happen. Steps 1–3 are custody's and are deliberately not + automated by this repository. +- **Consumer handoff verified.** ops-warden took option (a) for the reason we + gave rather than out of caution — the proxy yields an OpenBao token and + `keycape login` an issuer JWT, so they were never one thing to cut over + between. Lane `key-cape-oidc-login` is `owner-confirmed` with our boundary + written in verbatim, and `rapp-qonto-keycape-client` was narrowed to steps 1–2 + rather than cleared. No route was changed. + +*Executing* a rotation was never this task's deliverable, and treating the +owner's deferral as an open item would have kept a task open against work it was +never scoped to do. The deferral stands on its own above, revisited on evidence. + +Two residuals are recorded rather than carried here, because they belong to +whoever next opens a window: `real_predecessor_rotation_tested: false` and +`observed_wall_clock_expiry: false`. `verify-client -previous-secret-env` is +ready to close the first the moment a genuinely distinct predecessor exists. + +Also standing, from ops-warden and not to be lost: until WARDEN-WP-0038 lands, +**treat a `warden plan` verdict on any need containing a write, rotate or +provision act as unreliable** — it inherits the verdict of the lane that reads +the same path, and `autonomous` is documented as the signal to proceed without +the founder. That applies to this repository whenever it plans a rotation. diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0015-scope-intent-assessment.md b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0015-scope-intent-assessment.md new file mode 100644 index 00000000..82b952a2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1/consistency-backup/workplans/KEY-WP-0015-scope-intent-assessment.md @@ -0,0 +1,29 @@ +--- +id: KEY-WP-0015 +type: workplan +title: "Reconcile repository scope with implementation and intent" +domain: infotech +repo: key-cape +status: finished +owner: codex +topic_slug: scope-intent-assessment +created: "2026-09-05" +updated: "2026-09-05" +state_hub_workstream_id: "14a7312b-ec92-5640-8b26-cbaa4753c18e" +--- + +## Inventory implementation and document scope gaps + +```task +id: KEY-WP-0015-T01 +status: done +priority: medium +state_hub_task_id: "99be1489-d9e5-53b1-9c82-75dc961131f7" +``` + +Reviewed source revision b989de4, INTENT, SCOPE, runtime composition, authentication +commands, adapters, migration/validator code, tests, packaging and open workplans. +Updated SCOPE.md to describe implemented surfaces and their actual limits. +Recorded evidence, ten prioritized gaps and closure criteria in +history/2026-09-05-011726-scope-intent-assessment.md. INTENT and implementation are +unchanged. Findings are assessment results, not implementation completion claims.