From 7713a459239e75733cc41e0d358529327256050a Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 4 Oct 2026 08:59:02 +0200 Subject: [PATCH] Prepare informed-decision workload reader and contained metadata child Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1046b-5629-7f52-badd-f718eadb6042 --- credential-change-requests/CCR-2026-0032.yaml | 94 ++++++++++++ ...med-decision-requester-workload-input.yaml | 50 +++++++ .../informed-decision-requester-workload.md | 116 +++++++++++++++ ...-10-04-requester-workload-preparation.json | 42 ++++++ .../2026-10-04-1000/README.md | 24 +-- .../informed-decision/checklist.md | 9 ++ .../informed-decision/eso.draft.yaml | 54 +++++++ .../informed-decision/receipt-contract.json | 27 ++++ .../requester-mount.draft.yaml | 25 ++++ .../serviceaccount.draft.yaml | 10 ++ .../informed-decision/window.yaml | 7 + .../2026-10-04-1000/owner-returns.md | 44 ++++++ ...-decision-sitting-requester-eso.draft.json | 19 +++ ...nformed-decision-sitting-requester-eso.hcl | 3 + .../informed_decision_requester_metadata.py | 138 ++++++++++++++++++ ...st_informed_decision_requester_metadata.py | 120 +++++++++++++++ ...-0050-attended-provisioning-preparation.md | 15 +- ...ed-decision-requester-workload-delivery.md | 82 +++++++++++ 18 files changed, 868 insertions(+), 11 deletions(-) create mode 100644 credential-change-requests/CCR-2026-0032.yaml create mode 100644 docs/credential-lane-designs/informed-decision-requester-workload-input.yaml create mode 100644 docs/credential-lane-designs/informed-decision-requester-workload.md create mode 100644 docs/evidence/2026-10-04-requester-workload-preparation.json create mode 100644 docs/provisioning-windows/2026-10-04-1000/informed-decision/checklist.md create mode 100644 docs/provisioning-windows/2026-10-04-1000/informed-decision/eso.draft.yaml create mode 100644 docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json create mode 100644 docs/provisioning-windows/2026-10-04-1000/informed-decision/requester-mount.draft.yaml create mode 100644 docs/provisioning-windows/2026-10-04-1000/informed-decision/serviceaccount.draft.yaml create mode 100644 docs/provisioning-windows/2026-10-04-1000/informed-decision/window.yaml create mode 100644 docs/provisioning-windows/2026-10-04-1000/owner-returns.md create mode 100644 openbao/auth/informed-decision-sitting-requester-eso.draft.json create mode 100644 openbao/policies/workload-kv-read-informed-decision-sitting-requester-eso.hcl create mode 100644 scripts/informed_decision_requester_metadata.py create mode 100644 tests/test_informed_decision_requester_metadata.py create mode 100644 workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md diff --git a/credential-change-requests/CCR-2026-0032.yaml b/credential-change-requests/CCR-2026-0032.yaml new file mode 100644 index 0000000..b6b2f90 --- /dev/null +++ b/credential-change-requests/CCR-2026-0032.yaml @@ -0,0 +1,94 @@ +id: CCR-2026-0032 +kind: credential-change-request +schema_version: 1 +request_type: workload-kv-read +title: Informed Decision create-only requester workload read +status: proposed +created: '2026-10-04' +updated: '2026-10-04' +requester: + agent: informed-decision + reason: INFD-WP-0005-T05 request cd811073; prepare workload delivery of existing + requester custody +review: + required: true + required_approvers: + - platform-owner + - informed-decision-owner + comments: [] +target: + domain: financials + tenant: platform + workload: informed-decision + environment: production + purpose: Deliver existing create-only requester client to the review pod through + namespace-restricted ESO +openbao: + mount: platform + kv_path: platform/workloads/informed-decision/sitting-requester + fields: + - CLIENT_SECRET + policy_name: workload-kv-read-informed-decision-sitting-requester-eso + policy_file: openbao/policies/workload-kv-read-informed-decision-sitting-requester-eso.hcl + metadata_read: false + auth: + method: kubernetes + mount: kubernetes + role: informed-decision-sitting-requester-eso + audience: openbao + ttl: 15m + token_max_ttl: 15m + token_explicit_max_ttl: 15m + token_no_default_policy: true + policies: + - workload-kv-read-informed-decision-sitting-requester-eso + bound_claims: + service_account_names: + - informed-decision-eso + service_account_namespaces: + - informed-decision + bound_claims_confirmed: false +access_frontdoor: + type: external-secrets + catalog_id: informed-decision-sitting-requester-eso + readiness: pending-review + resolvable: false +risk: + classification: high + notes: + - The whole KV path is readable; the existing CLIENT_SECRET permits approval:create + for subject informed-decision + - Any ExternalSecret in the informed-decision namespace can reference this store; + no other namespace is admitted + - No approval:approve, approval:consume, sibling read, metadata listing or new client + is granted +verification: + positive: + - Owner-reviewed native exact identity and exact path proof required + negative: + - Owner-reviewed wrong identity, sibling GET and parent LIST denials required + activation_conditions: + - Exact CCR approval, confirmed identity, attended custody and native receipt acceptance + required +lifecycle: + deactivate: Owner-reviewed delivery withdrawal and token revocation; exact rollback + receipt required + rotate: Separate version-guarded owner approval and contained value delivery required + compromised: Owner incident process; no automatic read, reseed or cleanup +scaffold: + draft_only: true + consumer_repo: informed-decision + owner_repo: railiance-platform + request_sha256: 83181c92b80d708246147d6ee06dafdab880ce88f4203dfc7499970aa52e312f + catalog_request_sha256: null +delivery: + surface: external-secrets + target: informed-decision/informed-decision-sitting-requester key client-secret; + read-only projection into review pod + review_bundle: docs/provisioning-windows/2026-10-04-1000/informed-decision + metadata_child: scripts/informed_decision_requester_metadata.py +state_hub: + workplan_id: RPF-WP-0051 + task_id: RPF-WP-0051-T03 + related_workplan: INFD-WP-0005 + decision_api_url: http://127.0.0.1:8000/decisions/0d6792b6-59e4-4fa0-887a-c4e51ea8e131 diff --git a/docs/credential-lane-designs/informed-decision-requester-workload-input.yaml b/docs/credential-lane-designs/informed-decision-requester-workload-input.yaml new file mode 100644 index 0000000..821e45f --- /dev/null +++ b/docs/credential-lane-designs/informed-decision-requester-workload-input.yaml @@ -0,0 +1,50 @@ +schema_version: 1 +lane_id: informed-decision-sitting-requester-eso +ccr_id: CCR-2026-0032 +title: Informed Decision create-only requester workload read +date: '2026-10-04' +consumer_repo: informed-decision +owner_repo: railiance-platform +requester: + agent: informed-decision + reason: INFD-WP-0005-T05 request cd811073; prepare workload delivery of existing requester custody +review_approvers: +- platform-owner +- informed-decision-owner +target: + domain: financials + tenant: platform + workload: informed-decision + environment: production + purpose: Deliver existing create-only requester client to the review pod through namespace-restricted ESO +openbao: + mount: platform + kv_path: platform/workloads/informed-decision/sitting-requester + fields: + - CLIENT_SECRET + policy_name: workload-kv-read-informed-decision-sitting-requester-eso + auth: + mount: kubernetes + role: informed-decision-sitting-requester-eso + audience: openbao + service_account_names: + - informed-decision-eso + service_account_namespaces: + - informed-decision + ttl: 15m +eso: + store_name: openbao-informed-decision-sitting-requester + server: http://openbao.openbao.svc:8200 + namespace: informed-decision + external_secret_name: informed-decision-sitting-requester + secret_name: informed-decision-sitting-requester + refresh_interval: 5m + data: + - secret_key: client-secret + property: CLIENT_SECRET +risk: + classification: high + notes: + - The whole KV path is readable; the existing CLIENT_SECRET permits approval:create for subject informed-decision + - Any ExternalSecret in the informed-decision namespace can reference this store; no other namespace is admitted + - No approval:approve, approval:consume, sibling read, metadata listing or new client is granted diff --git a/docs/credential-lane-designs/informed-decision-requester-workload.md b/docs/credential-lane-designs/informed-decision-requester-workload.md new file mode 100644 index 0000000..7772aea --- /dev/null +++ b/docs/credential-lane-designs/informed-decision-requester-workload.md @@ -0,0 +1,116 @@ +# Informed-decision requester workload delivery + +Status: proposed source, CCR-2026-0032, RPF-WP-0051. Demand: INFD-WP-0005-T05, +message cd811073-d6db-41cf-8a57-10df0b360eab. Source preparation is authorized by +the founder's RPF-WP-0050 scope selection and continuation instruction. + +## Exact proposed boundary + +The existing KV-v2 path `platform/workloads/informed-decision/sitting-requester` +and field `CLIENT_SECRET` remain issuer custody. New policy +`workload-kv-read-informed-decision-sitting-requester-eso` reads only its exact +data path. It grants no KV write, metadata read/list, wildcard, self-lifecycle +or sibling access. KV ACLs apply to the entire path, not individual fields; +future fields at this path require review of the reader's risk. + +The prospective auth role `informed-decision-sitting-requester-eso` binds only +ServiceAccount `informed-decision/informed-decision-eso` and audience `openbao`. +Token TTL, maximum and explicit maximum are 900 seconds, with no default policy +or periodic token. The review Deployment retains its own ServiceAccount `review`; +the new ServiceAccount serves ESO authentication only. ESO's actual TokenRequest +permission for this account must be verified through the owner before promotion. + +The proposed ClusterSecretStore `openbao-informed-decision-sitting-requester` +admits only namespace `informed-decision`. The proposed ExternalSecret and target +Secret are `informed-decision-sitting-requester`, key `client-secret`, refreshed +every five minutes. Explicit target annotations contain source annotation copying. +Any ExternalSecret in this namespace can use the store; it is a namespace boundary, +not pod-level admission. Namespace writer access remains part of owner review. + +The mount proposal projects only that key at +`/var/run/secrets/informed-decision/requester/client-secret`, read-only, mode 0440, +without subPath or environment delivery. The existing review pod runs as +UID/GID/fsGroup 10001. Kubernetes rotation changes the projected file; the owner +callback must reopen it per exchange rather than caching the credential forever. +Missing custody must fail closed and the required Secret projection is not optional. + +## Runtime composition remains an owner gate + +The owner implements `IntakeApprovalClient` with an injected access-token callback; +the deployed runtime does not yet compose the private intake listener or this +file-backed client-credentials exchange. This packet deliberately introduces no +invented environment setting or runtime JSON key. The owner must accept and compose +the file callback, exact issuer/token endpoint, bounded transport with CA checks, +RS256/JWKS signature verification, and claim/scope refusal before promotion. + +Existing verified requester contract: client `informed-decision-sitting-requester`, +subject `informed-decision`, audience `approval-engine`, tenant `tenant:platform`, +service principal, role `informed-decision-sitting-requester`, and scope exactly +`approval:create`. The maximum existing JWT lifetime is fifteen minutes. Human +answering uses the existing browser client; no approval/read/consume scope or +workload acceptance route is introduced. No client registration, custody seed or +rotation occurs in this work. Existing CCR-2026-0026/0027 remain applied. + +## Concrete metadata child for review + +`scripts/informed_decision_requester_metadata.py` is a silent contained child, +not yet approved to run live. It requires the actual CCR's SHA256, approved +status and confirmed exact binding, and an attended platform-admin identity +without root or ambient token environment overrides. It checks both current +policy and role before any write, refuses differing objects, writes only missing +metadata objects from stdin, and verifies both afterward. Only an API 404 means +absence; denial and transport errors stop the operation. It neither reads/writes +KV values nor applies Kubernetes objects, and retains no token or subprocess +output in a file or receipt. + +After owner acceptance, exact CCR/binding approval and fresh founder go, review +the exact helper and policy digests and run from that checkout in the founder's +terminal: + +```sh +BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 \ + warden access openbao-platform-admin-login --exec -- \ + python3 scripts/informed_decision_requester_metadata.py \ + --expected-ccr-sha256 +``` + +The argument binds the approved CCR, whose status/binding changes produce a new +digest; the current proposed digest is not an execution authorization. Exit 0 +means metadata readback passed; 2 means source/approval/envelope refusal; 3 means +an existing object differs; 4 means command failure; 5 means malformed response +or readback failure. The founder retains warden's actual completion/revocation +line; its exit code alone does not establish handoff or session cleanup. +Metadata success is not native reader or runtime acceptance. + +The review manifests live in +`docs/provisioning-windows/2026-10-04-1000/informed-decision/`. No active +Kustomization references them. ESO/runtime promotion needs its own owner path, +cluster/node and headroom checks, diff/server dry-run and approved immutable +image/config. Neither this command nor the packet activates that promotion. + +## Owner receipt and abort contract + +Record only non-secret identities, timestamps, source digests, readiness and +capability booleans in `receipt-contract.json` or its accepted successor. Native +checks must prove exact role identity/effective policy and all TTL bounds; ESO +SecretSynced; safe annotation metadata; exact-file projection; sibling data GET +and parent metadata LIST denial; wrong namespace/ServiceAccount/audience denial; +and create-only exchange with approve/read/consume refused. Never print a real +Secret, annotation contents, token/accessor or raw child error. Use the maintained +secret-annotation helper for actual Secret metadata checks. + +Rotation review must prove the callback observes a changed file and stops using +the predecessor after coordinated issuer rotation. No issuer rotation is +performed merely to prove metadata preparation. Reader token expiry/revocation +and attended admin-session cleanup require native receipts. + +The founder is the proposed driver and abort owner for the attended metadata +step; confirm attendance at the actual window. The receiving owner must name +its runtime verification/abort owner before promotion. On a partial failure, +stop and establish actual state; do not overwrite conflicts or automatically +delete objects. Owner-reviewed rollback withdraws this new ESO/runtime projection +and disables only this new reader role, revoking its outstanding tokens through +admin custody. Already materialized target Secrets need separately approved +removal; `deletionPolicy: Retain` does not erase them. Existing verifier and +attended-reader roles and the KeyCape client remain intact. If value exposure +occurred, use the separate issuer incident/rotation procedure. diff --git a/docs/evidence/2026-10-04-requester-workload-preparation.json b/docs/evidence/2026-10-04-requester-workload-preparation.json new file mode 100644 index 0000000..d32e0ce --- /dev/null +++ b/docs/evidence/2026-10-04-requester-workload-preparation.json @@ -0,0 +1,42 @@ +{ + "schema": "railiance-platform.requester-workload-source-review.v1", + "task": "RPF-WP-0051-T01", + "date": "2026-10-04", + "status": "source-prepared", + "ccr_status": "proposed", + "identity_confirmed": false, + "owner_runtime_accepted": false, + "native_checks_executed": false, + "credential_values_accessed": false, + "production_changed": false, + "active_gitops_references": false, + "helper_tests_passed": 10, + "scaffold_regression_tests_passed": 14, + "window_regression_tests_passed": 7, + "actual_bundle_invariants_passed": true, + "separate_decision_checklist_ready": false, + "artifacts": { + "credential-change-requests/CCR-2026-0032.yaml": "e54fa5d21c7444c5f248c6cfe005b7f96888430ef857fcd914cc8cd7dabf18ce", + "docs/credential-lane-designs/informed-decision-requester-workload-input.yaml": "83181c92b80d708246147d6ee06dafdab880ce88f4203dfc7499970aa52e312f", + "docs/credential-lane-designs/informed-decision-requester-workload.md": "b34418998ab9c0c4086d4de7ecac2368a16a0d05738a622d5fc2c208477253dc", + "openbao/policies/workload-kv-read-informed-decision-sitting-requester-eso.hcl": "9ba5de62946695bf6afb974f338fb0fe6193c742575cc7af4171279445528cea", + "openbao/auth/informed-decision-sitting-requester-eso.draft.json": "112c3f4a7561052b7d45eecc459ec30bd13558b76ad6d6badc6c79ca7dfcdc74", + "scripts/informed_decision_requester_metadata.py": "ddad76dc6bcd3de62158a4225eb481dda87a044d88a3c21e8344a0aa379d8fc5", + "tests/test_informed_decision_requester_metadata.py": "8f5db33298d0dad516f866daf451f04e7154b937aa737f198aaceb103f433bd8", + "docs/provisioning-windows/2026-10-04-1000/informed-decision/checklist.md": "f0f2f73a242930b6f5cffb396828f0ec06c417e3e21593dbfdd93822c8e4f62b", + "docs/provisioning-windows/2026-10-04-1000/informed-decision/eso.draft.yaml": "1a3e3cebeb655c409ba730d31c6c398386dbbfd1c0f8291fc676d4219af4b62c", + "docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json": "46ec57055742aaf1295a00d7db7072af94f688a4ed2b29bab711426252ab79a7", + "docs/provisioning-windows/2026-10-04-1000/informed-decision/requester-mount.draft.yaml": "3dc885ea4b10bfb5a06df5fddd722910410788e4c530cadb8c1bd1692a9f9e81", + "docs/provisioning-windows/2026-10-04-1000/informed-decision/serviceaccount.draft.yaml": "2168231400ae29e3d7a79484fbabc805569b76999f6b6473abb65707b6ddfac7", + "docs/provisioning-windows/2026-10-04-1000/informed-decision/window.yaml": "376f990d03f49fbbfaa2e83ad56c61e1559360c3531ff8a9a2b02077f9f61bbd" + }, + "owner_sources": { + "/home/worsch/informed-decision/docs/intake-dependency-requirements.md": "7cd6b91e1d14421f7c50eceb60ef7e42a63bda29314604cff5234c72c6bc423a", + "/home/worsch/informed-decision/tools/render_deployment.py": "cab80a90fba254bceab50d77b4acefac6d5c23fef171a18e0688b0dd2e21c959", + "/home/worsch/informed-decision/tools/create_sitting_approvals.py": "0c13da683c5ad7f69afbf0cd500609a47b546e29de61decf7464cbafc181e234", + "/home/worsch/informed-decision/informed_decision/intake_approval.py": "52f8b3643e4c5c6f56f2ef1fd9c7747da2165f8f88122e93b3f3ee3ecae48c73", + "/home/worsch/informed-decision/informed_decision/runtime.py": "4768f6261510fcb8f7bfaa01dc648fb9bbe13d5389a67b31810cb4b1917694d4", + "/home/worsch/user-engine/workplans/USER-WP-0035-onboarding-handoff-findings.md": "1b59a99ef4b9743b02646b6201ade9ed0e4f504f3d3c571890f18d39be6994b2" + }, + "mail_correction": "USER-WP-0035 2026-09-27 qualification confirms mail exists; missing provider setup-link handoff, not a new mail credential" +} diff --git a/docs/provisioning-windows/2026-10-04-1000/README.md b/docs/provisioning-windows/2026-10-04-1000/README.md index 2562b93..f3aeb0d 100644 --- a/docs/provisioning-windows/2026-10-04-1000/README.md +++ b/docs/provisioning-windows/2026-10-04-1000/README.md @@ -9,8 +9,8 @@ using the selected existing batched attended custody. No recurrence is selected. | Order | Lane / owning task | Relevance | Present readiness and required return | | --- | --- | --- | --- | | 1 | secrets-engine service JWT login / RPF-WP-0035-T02 | Enables the service's bounded identity login for credential workflows | NOT READY. KeyCape must confirm live `secrets-engine-openbao` registration and its separately approved client custody. Platform then reviews the isolated mount/config/self-policy/role child and receipt. The current workload-read CCR type cannot encode this JWT authority. | -| 2 | informed-decision sitting-requester workload delivery / INFD-WP-0005-T05 | Removes the attended secret-read dependency for decision intake | NOT READY. Requires a separately reviewed exact-path workload reader and namespace-restricted ESO delivery into the review pod. CCR-2026-0026/0027 are applied historical custody, not authority for this new reader. Founder per-change go and owner runtime acceptance remain. | -| 3 | email-connect transactional delivery / USER-WP-0035-T02 | Enables governed account/invitation mail delivery | NOT READY. CCR-2026-0010 is active historical custody, not an approved new provisioning candidate. email-connect/user-engine must identify the exact remaining delivery-token/runtime work, reviewed command, caller scope and acceptance receipt. Do not recreate SMTP custody. | +| 2 | informed-decision sitting-requester workload delivery / INFD-WP-0005-T05 | Removes the attended secret-read dependency for decision intake | SOURCE PREPARED, NOT LIVE READY. Proposed CCR-2026-0032, exact policy/role, ESO, auth ServiceAccount, mount proposal and silent conflict-refusing child are now prepared under RPF-WP-0051. Owner runtime/binding acceptance and founder per-change go remain. CCR-2026-0026/0027 are applied historical custody. | +| 3 | provider password-setup-link delivery / USER-WP-0035-T02 | Enables delivery of the account setup handoff to its intended recipient | OWNER IMPLEMENTATION REQUIRED. The 2026-09-27 source qualification confirms authenticated mail already exists. Missing work is the provider-supported setup-link handoff and recipient receipt, not new SMTP/token provisioning. | These are preparation priorities, not three approved execution commands. Source contracts: `docs/credential-lane-designs/secrets-engine-service-jwt.md`, @@ -36,11 +36,10 @@ Return delivery health, sibling-path/list denial, read-audit and rotation/cleanu evidence without values. Listener/admission/forwarding rollout is separate work and is not authorized by selecting this lane. -For mail, the receiving owners must distinguish existing SMTP custody from the -remaining caller/token delivery. Return the exact approved caller and target, -native bounded delivery success, unauthorized caller denial, expiry/revocation -and protected-file cleanup evidence. No receipt or rollback command can be -invented before that contract is returned. +For setup-link delivery, the provider/user-engine owners must return the exact +handoff contract and recipient delivery evidence. Existing SMTP/invitation proof +does not establish delivery of the provider's single-use setup link. No receipt +or rollback command can be invented before that contract is returned. Each actual item must have a named driver/abort owner, exact revision/digest, expected result and rollback before founder execution approval. The founder runs @@ -70,9 +69,16 @@ joins. `source-review.json` pins the authoritative roots/files and verifies each open D1 task's Hub identity, status and decision join. No complete or live-ready claim follows from source review. Recheck revisions and owner returns at 10:00. +The selected new reader's separate `informed-decision/window.yaml` joins its own +decision to proposed CCR-2026-0032, lane design and receipt contract. It remains +NOT READY while approval/binding/runtime acceptance are outstanding. The silent +metadata child is source-prepared; it has not run live. `owner-returns.md` lists +the exact external acts for the three priorities. + Custodian residual CUST-WP-0076-T01 now has the founder's concrete date/time, one-time cadence and delegated relevance selection in this packet. Its owner must review/record that return in its own source; platform does not mark the -Custodian task done. RPF-WP-0050-T03 remains waiting on owner mappings, reviewed -commands and Custodian handoff review. D1 is not resolved and no automation or +Custodian task done. Direct founder selection supplies platform's date/scope +gate; RPF-WP-0050-T03 remains waiting on actual owner acceptance and commands. +D1 is not resolved and no automation or live change is activated. diff --git a/docs/provisioning-windows/2026-10-04-1000/informed-decision/checklist.md b/docs/provisioning-windows/2026-10-04-1000/informed-decision/checklist.md new file mode 100644 index 0000000..6b5262e --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/informed-decision/checklist.md @@ -0,0 +1,9 @@ +# Attended provisioning preparation: 2026-10-04 +Decision: 0d6792b6-59e4-4fa0-887a-c4e51ea8e131 +Preparation only. Founder window approval and each exact CCR revision remain required. +1. INFD-WP-0005-T05: NOT READY (CCR is not approved; never replay applied custody) + CCR: credential-change-requests/CCR-2026-0032.yaml + Lane: docs/credential-lane-designs/informed-decision-requester-workload.md + Owner receipt contract: docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json + Revalidate exact digest, upstream gates and native readiness before attended execution. +Checklist complete: false diff --git a/docs/provisioning-windows/2026-10-04-1000/informed-decision/eso.draft.yaml b/docs/provisioning-windows/2026-10-04-1000/informed-decision/eso.draft.yaml new file mode 100644 index 0000000..632419a --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/informed-decision/eso.draft.yaml @@ -0,0 +1,54 @@ +# DRAFT REVIEW ARTIFACT. Do not apply. No custody/admission supplied. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao-informed-decision-sitting-requester + annotations: + railiance.io/credential-change: CCR-2026-0032 + railiance.io/admission: pending-review +spec: + provider: + vault: + server: http://openbao.openbao.svc:8200 + path: platform + version: v2 + auth: + kubernetes: + mountPath: kubernetes + role: informed-decision-sitting-requester-eso + audiences: + - openbao + serviceAccountRef: + name: informed-decision-eso + namespace: informed-decision + conditions: + - namespaces: + - informed-decision +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: informed-decision-sitting-requester + namespace: informed-decision + annotations: + railiance.io/credential-change: CCR-2026-0032 + railiance.io/admission: pending-review +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-informed-decision-sitting-requester + target: + name: informed-decision-sitting-requester + creationPolicy: Owner + deletionPolicy: Retain + template: + metadata: + annotations: + railiance.io/credential-change: CCR-2026-0032 + railiance.io/admission: pending-review + data: + - secretKey: client-secret + remoteRef: + key: workloads/informed-decision/sitting-requester + property: CLIENT_SECRET diff --git a/docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json b/docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json new file mode 100644 index 0000000..bf1828e --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json @@ -0,0 +1,27 @@ +{ + "schema_version": 1, + "lane_id": "informed-decision-sitting-requester-eso", + "ccr_id": "CCR-2026-0032", + "status": "not-executed", + "draft_only": true, + "verified": false, + "request_sha256": "83181c92b80d708246147d6ee06dafdab880ce88f4203dfc7499970aa52e312f", + "required_owner_checks": [ + "Exact revision/digest and required owner reviews", + "Identity/auth claims confirmed separately; no inferred workload identity", + "Metadata/capability readback through reviewed output-suppressing owner tool", + "No Secret bodies, values or raw subprocess errors in evidence", + "Native scoped positive/negative, cleanup/revocation and rollback receipts after approval", + "ESO TokenRequest authority for informed-decision/informed-decision-eso", + "Effective role policy and TTL/max/explicit max 900s with default policy absent", + "Wrong SA/namespace/audience and sibling data GET/parent metadata LIST refused", + "SecretSynced, safe annotation helper and exact read-only file projection", + "Native RS256 requester exchange: exact subject/audience/tenant/role; approval:create only; excess scopes refused", + "Rotating callback rereads projection; issuer predecessor refusal under separate approved rotation", + "Reader expiry/revocation, attended-session cleanup and retained target Secret disposition", + "Owner immutable runtime/config and named verification/abort owner" + ], + "helper": "scripts/informed_decision_requester_metadata.py", + "proposed_driver_and_metadata_abort_owner": "Bernd Worsch; confirm attendance at execution window", + "runtime_verification_owner": null +} diff --git a/docs/provisioning-windows/2026-10-04-1000/informed-decision/requester-mount.draft.yaml b/docs/provisioning-windows/2026-10-04-1000/informed-decision/requester-mount.draft.yaml new file mode 100644 index 0000000..fb71bb1 --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/informed-decision/requester-mount.draft.yaml @@ -0,0 +1,25 @@ +# Strategic merge REVIEW PROPOSAL only; no runtime setting is invented. +# The owner must compose its file-backed client-credentials callback before rollout. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: informed-decision + namespace: informed-decision +spec: + template: + spec: + containers: + - name: informed-decision + volumeMounts: + - name: sitting-requester + mountPath: /var/run/secrets/informed-decision/requester + readOnly: true + volumes: + - name: sitting-requester + secret: + secretName: informed-decision-sitting-requester + defaultMode: 0440 + optional: false + items: + - key: client-secret + path: client-secret diff --git a/docs/provisioning-windows/2026-10-04-1000/informed-decision/serviceaccount.draft.yaml b/docs/provisioning-windows/2026-10-04-1000/informed-decision/serviceaccount.draft.yaml new file mode 100644 index 0000000..6e57667 --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/informed-decision/serviceaccount.draft.yaml @@ -0,0 +1,10 @@ +# REVIEW ONLY: not included in any GitOps rendering. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: informed-decision-eso + namespace: informed-decision + annotations: + railiance.io/credential-change: CCR-2026-0032 + railiance.io/admission: pending-review +automountServiceAccountToken: false diff --git a/docs/provisioning-windows/2026-10-04-1000/informed-decision/window.yaml b/docs/provisioning-windows/2026-10-04-1000/informed-decision/window.yaml new file mode 100644 index 0000000..811e74b --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/informed-decision/window.yaml @@ -0,0 +1,7 @@ +decision_id: 0d6792b6-59e4-4fa0-887a-c4e51ea8e131 +date: '2026-10-04' +entries: +- task_id: INFD-WP-0005-T05 + ccr: credential-change-requests/CCR-2026-0032.yaml + lane_doc: docs/credential-lane-designs/informed-decision-requester-workload.md + receipt_contract: docs/provisioning-windows/2026-10-04-1000/informed-decision/receipt-contract.json diff --git a/docs/provisioning-windows/2026-10-04-1000/owner-returns.md b/docs/provisioning-windows/2026-10-04-1000/owner-returns.md new file mode 100644 index 0000000..3583c03 --- /dev/null +++ b/docs/provisioning-windows/2026-10-04-1000/owner-returns.md @@ -0,0 +1,44 @@ +# Remaining owner acts for the selected window + +These are source-review requests, not completed acknowledgements or approved +live actions. Platform does not change another owner's workplan or send values. + +## secrets-engine / KeyCape — RPF-WP-0035-T02 + +Platform already has exact proposed files `openbao/auth/keycape-services-config.json`, +`openbao/auth/secrets-engine-jwt-role.json` and +`openbao/policies/secrets-engine-login-self.hcl`. The design admits only login and +self inspection/revocation, not lane apply. SECRETS-WP-0007-T04 is done. + +KeyCape must name the owning live registration and protected-client-custody work +record, confirm the actual `tenant:platform` claims and supply non-secret custody +coordinates. The provider contract currently confirms a source registration only. +There is no value-safe justification to construct a live client or assume its +credential path from those examples. Platform must then complete/review the +isolated mount/config/role metadata child and native verification against the +owner's actual receipt. Acceptance/expiry/use-limit/revocation proof remains in +the existing design. This prerequisite cannot be supplied by the workload-read +CCR generator or a generic attended approval. + +## informed-decision — INFD-WP-0005-T05 / RPF-WP-0051 + +New concrete source packet: proposed CCR-2026-0032, +`docs/credential-lane-designs/informed-decision-requester-workload.md`, and +`informed-decision/` in this window. Confirm the ESO auth ServiceAccount, actual +TokenRequest authority, target Secret/mount and receiving runtime owner. +Compose the file-backed client-credentials callback and private intake service +in the owner runtime. Return immutable image/config and the native proof plan. +Listener/admission/TokenReview/forwarding gates remain owner dependencies. +Existing requester custody is reused; its applied CCRs are never reseeded. + +## user-engine / provider — USER-WP-0035-T02 + +The 2026-09-27 qualification in USER-WP-0035 supersedes its generic blocking +summary: authenticated mail exists, with live non-sending SMTP/store checks. +EMAIL-WP-0004-T03 is done and USER-WP-0032/P05 handles invitation outbox delivery. +The missing act is a provider-supported handoff of the single-use password-setup +link and evidence that the intended person receives it. Generic SMTP success, +reprovisioning CCR-2026-0010 or copying the link into a work record cannot close it. +The provider/user-engine owners must define and implement that delivery contract +without exposing the setup-link value. There is no new platform credential +provisioning action identified for this task. diff --git a/openbao/auth/informed-decision-sitting-requester-eso.draft.json b/openbao/auth/informed-decision-sitting-requester-eso.draft.json new file mode 100644 index 0000000..5213960 --- /dev/null +++ b/openbao/auth/informed-decision-sitting-requester-eso.draft.json @@ -0,0 +1,19 @@ +{ + "draft_only": true, + "status": "proposed", + "bound_claims_confirmed": false, + "auth_payload": { + "bound_service_account_names": [ + "informed-decision-eso" + ], + "bound_service_account_namespaces": [ + "informed-decision" + ], + "policies": "workload-kv-read-informed-decision-sitting-requester-eso", + "ttl": "15m", + "audience": "openbao", + "token_max_ttl": "15m", + "token_explicit_max_ttl": "15m", + "token_no_default_policy": true + } +} diff --git a/openbao/policies/workload-kv-read-informed-decision-sitting-requester-eso.hcl b/openbao/policies/workload-kv-read-informed-decision-sitting-requester-eso.hcl new file mode 100644 index 0000000..8d8d4a0 --- /dev/null +++ b/openbao/policies/workload-kv-read-informed-decision-sitting-requester-eso.hcl @@ -0,0 +1,3 @@ +path "platform/data/workloads/informed-decision/sitting-requester" { + capabilities = ["read"] +} diff --git a/scripts/informed_decision_requester_metadata.py b/scripts/informed_decision_requester_metadata.py new file mode 100644 index 0000000..c540ba9 --- /dev/null +++ b/scripts/informed_decision_requester_metadata.py @@ -0,0 +1,138 @@ +"""Silent, attended, compare-before-write metadata child for CCR-2026-0032. + +Does not read/write KV values, change issuer custody, or deploy Kubernetes objects. +""" +import argparse +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import subprocess +import sys + +ROOT = Path(__file__).resolve().parents[1] +CCR = ROOT / 'credential-change-requests/CCR-2026-0032.yaml' +POLICY = 'workload-kv-read-informed-decision-sitting-requester-eso' +ROLE = 'auth/kubernetes/role/informed-decision-sitting-requester-eso' + + +class Refused(Exception): + def __init__(self, code): + self.code = code + + +def cli(arguments, payload=None, *, missing_ok=False): + try: + result = subprocess.run(['bao', *arguments], input=payload, + capture_output=True, timeout=30, check=False) + except (OSError, subprocess.TimeoutExpired): + raise Refused(4) from None + if result.returncode: + # Only a structured API 404 is absence. Denial/transport failure is not. + if missing_ok and result.returncode == 2 and b'Code: 404' in result.stderr: + return None + raise Refused(4) + if arguments[0] == 'write' and not result.stdout.strip(): + return {} + try: + return json.loads(result.stdout) + except (ValueError, UnicodeError): + raise Refused(5) from None + + +def role_matches(value): + expected = { + 'bound_service_account_names': ['informed-decision-eso'], + 'bound_service_account_namespaces': ['informed-decision'], + 'audience': 'openbao', 'token_policies': [POLICY], + 'token_ttl': 900, 'token_max_ttl': 900, + 'token_explicit_max_ttl': 900, 'token_no_default_policy': True, + } + return (isinstance(value, dict) + and all(type(value.get(k)) is type(v) and value[k] == v for k, v in expected.items()) + and value.get('token_period', 0) == 0 + and value.get('bound_service_account_namespace_selector', '') == '' + and value.get('token_type', 'default') in ('default', 'service')) + + +def source(expected_digest): + if hashlib.sha256(CCR.read_bytes()).hexdigest() != expected_digest: + raise Refused(2) + spec = importlib.util.spec_from_file_location('credential_change', ROOT / 'scripts/credential-change.py') + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + ccr, errors, _ = module.validate_ccr(CCR) + if errors or ccr['status'] != 'approved' or ccr['openbao']['auth'].get('bound_claims_confirmed') is not True: + raise Refused(2) + bao = ccr['openbao'] + auth = bao['auth'] + if (bao['policy_name'] != POLICY or bao['kv_path'] != 'platform/workloads/informed-decision/sitting-requester' + or bao['fields'] != ['CLIENT_SECRET'] or bao.get('metadata_read') is not False + or bao.get('token_self_lifecycle', False) is not False + or auth['method'] != 'kubernetes' or auth['mount'] != 'kubernetes' + or auth['role'] != ROLE.rsplit('/', 1)[1]): + raise Refused(2) + payload = module.auth_payload(ccr) + if payload != {'bound_service_account_names': ['informed-decision-eso'], + 'bound_service_account_namespaces': ['informed-decision'], + 'policies': POLICY, 'ttl': '15m', 'audience': 'openbao', + 'token_max_ttl': '15m', 'token_explicit_max_ttl': '15m', + 'token_no_default_policy': True}: + raise Refused(2) + policy = module.generated_policy_hcl(ccr) + if (ROOT / bao['policy_file']).read_text() != policy: + raise Refused(2) + return policy, payload + + +def apply(policy, payload, *, command=cli): + current_policy = command(['read', '-format=json', 'sys/policies/acl/' + POLICY], missing_ok=True) + current_role = command(['read', '-format=json', ROLE], missing_ok=True) + # Check BOTH before mutating either; differing objects require a fresh review. + if current_policy is not None and current_policy.get('data', {}).get('policy') != policy: + raise Refused(3) + if current_role is not None and not role_matches(current_role.get('data')): + raise Refused(3) + if current_policy is None: + command(['write', '-format=json', 'sys/policies/acl/' + POLICY, '-'], + json.dumps({'policy': policy}).encode()) + if current_role is None: + command(['write', '-format=json', ROLE, '-'], json.dumps(payload).encode()) + verified_policy = command(['read', '-format=json', 'sys/policies/acl/' + POLICY]) + verified_role = command(['read', '-format=json', ROLE]) + if (verified_policy.get('data', {}).get('policy') != policy + or not role_matches(verified_role.get('data'))): + raise Refused(5) + + +def main(): + try: + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('--expected-ccr-sha256', required=True) + # Parsing is deliberately silent even on malformed arguments. + with open(os.devnull, 'w') as sink: + original = sys.stderr + try: + sys.stderr = sink + args = parser.parse_args() + finally: + sys.stderr = original + if (Path.home().parent.name != '.warden-attended-login' + or os.getenv('BAO_TOKEN') or os.getenv('VAULT_TOKEN')): + raise Refused(2) + policy, payload = source(args.expected_ccr_sha256) + identity = cli(['token', 'lookup', '-format=json']).get('data', {}) + policies = identity.get('policies', []) + if 'platform-admin' not in policies or 'root' in policies: + raise Refused(2) + apply(policy, payload) + return 0 + except Refused as exc: + return exc.code + except BaseException: + return 2 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/test_informed_decision_requester_metadata.py b/tests/test_informed_decision_requester_metadata.py new file mode 100644 index 0000000..bb8899a --- /dev/null +++ b/tests/test_informed_decision_requester_metadata.py @@ -0,0 +1,120 @@ +import contextlib +import importlib.util +import io +import json +from pathlib import Path +import subprocess +import unittest +from unittest.mock import patch + +spec = importlib.util.spec_from_file_location('requester_metadata', Path(__file__).resolve().parents[1] / 'scripts/informed_decision_requester_metadata.py') +m = importlib.util.module_from_spec(spec) +spec.loader.exec_module(m) + + +def role(**updates): + return dict(bound_service_account_names=['informed-decision-eso'], + bound_service_account_namespaces=['informed-decision'], audience='openbao', + token_policies=[m.POLICY], token_ttl=900, token_max_ttl=900, + token_explicit_max_ttl=900, token_no_default_policy=True, **updates) + + +class MetadataTests(unittest.TestCase): + def test_role_rejects_widening_and_unbounded_token(self): + original = role() + for key, value in [('bound_service_account_names', ['*']), + ('bound_service_account_namespaces', ['*']), + ('audience', 'other'), ('token_policies', [m.POLICY, 'default']), + ('token_ttl', 3600), ('token_max_ttl', 0), + ('token_explicit_max_ttl', 0), ('token_no_default_policy', 1), + ('token_period', 900), ('bound_service_account_namespace_selector', '*')]: + with self.subTest(key=key): + self.assertFalse(m.role_matches(dict(original, **{key: value}))) + self.assertTrue(m.role_matches(original)) + + def test_differing_role_refuses_before_missing_policy_write(self): + calls = [] + def command(args, payload=None, **kwargs): + calls.append(args) + return None if args[-1].startswith('sys/') else {'data': dict(role(), audience='wrong')} + with self.assertRaises(m.Refused) as caught: + m.apply('exact policy', {}, command=command) + self.assertEqual(caught.exception.code, 3) + self.assertTrue(all(args[0] == 'read' for args in calls)) + + def test_differing_policy_refuses_all_writes(self): + calls = [] + def command(args, payload=None, **kwargs): + calls.append(args) + return {'data': {'policy': 'different'}} if args[-1].startswith('sys/') else None + with self.assertRaises(m.Refused) as caught: + m.apply('exact policy', {}, command=command) + self.assertEqual(caught.exception.code, 3) + self.assertTrue(all(args[0] == 'read' for args in calls)) + + def test_identical_objects_noop_without_write(self): + calls = [] + def command(args, payload=None, **kwargs): + calls.append(args) + return {'data': {'policy': 'exact policy'}} if args[-1].startswith('sys/') else {'data': role()} + m.apply('exact policy', {}, command=command) + self.assertEqual(len(calls), 4) + self.assertTrue(all(args[0] == 'read' for args in calls)) + + def test_new_objects_write_only_two_metadata_paths_then_verify(self): + calls = [] + saved = {} + def command(args, payload=None, **kwargs): + calls.append(args) + path = args[2] + if args[0] == 'write': + saved[path] = json.loads(payload) + return {} + if path not in saved: + return None + return {'data': saved[path]} if path.startswith('sys/') else {'data': role()} + m.apply('exact policy', {'audience': 'openbao'}, command=command) + self.assertEqual(set(saved), {'sys/policies/acl/' + m.POLICY, m.ROLE}) + self.assertEqual([args[0] for args in calls], ['read', 'read', 'write', 'write', 'read', 'read']) + + def test_postwrite_role_drift_fails_verification(self): + calls = [] + def command(args, payload=None, **kwargs): + calls.append(args) + if kwargs.get('missing_ok'): return None + if args[0] == 'write': return {} + return {'data': {'policy': 'exact policy'}} if args[-1].startswith('sys/') else {'data': dict(role(), audience='wrong')} + with self.assertRaises(m.Refused) as caught: + m.apply('exact policy', {}, command=command) + self.assertEqual(caught.exception.code, 5) + + def test_404_only_is_absence_and_failures_are_silent(self): + for code, stderr, missing in [(2, b'Code: 404', True), (2, b'Code: 403 SECRET-SENTINEL', False), (1, b'Code: 404 SECRET-SENTINEL', False)]: + with self.subTest(code=code, stderr=stderr), patch.object(m.subprocess, 'run', return_value=subprocess.CompletedProcess([], code, b'SECRET-SENTINEL', stderr)): + output = io.StringIO() + with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output): + if missing: self.assertIsNone(m.cli(['read', '-format=json', m.ROLE], missing_ok=True)) + else: + with self.assertRaises(m.Refused): m.cli(['read', '-format=json', m.ROLE], missing_ok=True) + self.assertEqual(output.getvalue(), '') + + def test_empty_204_write_is_success(self): + with patch.object(m.subprocess, 'run', return_value=subprocess.CompletedProcess([], 0, b'', b'')): + self.assertEqual(m.cli(['write', '-format=json', m.ROLE, '-'], b'{}'), {}) + + def test_proposed_source_refuses_before_network(self): + digest = m.hashlib.sha256(m.CCR.read_bytes()).hexdigest() + with patch.object(m.subprocess, 'run', side_effect=AssertionError('must remain offline')): + with self.assertRaises(m.Refused) as caught: m.source(digest) + self.assertEqual(caught.exception.code, 2) + + def test_missing_arguments_silent(self): + with patch.object(m.sys, 'argv', ['metadata-child']): + output = io.StringIO() + with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output): + self.assertEqual(m.main(), 2) + self.assertEqual(output.getvalue(), '') + + +if __name__ == '__main__': + unittest.main() diff --git a/workplans/RPF-WP-0050-attended-provisioning-preparation.md b/workplans/RPF-WP-0050-attended-provisioning-preparation.md index 09f6bb5..950a92b 100644 --- a/workplans/RPF-WP-0050-attended-provisioning-preparation.md +++ b/workplans/RPF-WP-0050-attended-provisioning-preparation.md @@ -46,8 +46,8 @@ P3 options and required review boundaries are drafted in the contract. Existing id: RPF-WP-0050-T03 status: wait priority: medium -blocked_on: message-from:the-custodian -blocking_reason: "Founder selected 2026-10-04 10:00 Europe/Berlin and delegated lane relevance selection. Dated packet is prepared; Custodian handoff review and exact owner contracts/commands/receipt mappings for the selected priorities remain before a complete manifest. Exact founder execution authorization remains separate." +depends_on: [RPF-WP-0051-T02, RPF-WP-0035-T02, USER-WP-0035-T02] +blocking_reason: "Direct founder selection supplies the one-time date/scope. Informed-decision reader source is prepared under RPF-WP-0051; exact owner runtime/binding acceptance, KeyCape service registration/custody and provider setup-link handoff remain. Exact founder execution authorization remains separate." state_hub_task_id: "1ae5a6b8-9c83-5813-982f-ca8ad24be8b5" ``` @@ -55,6 +55,17 @@ Generate a checklist from all named authoritative roots, verified Hub decision j 2026-10-04: Bernd directed "Pick lanes by relevance for today 10am". Prepared the one-time 10:00 Europe/Berlin (08:00 UTC) source packet in `docs/provisioning-windows/2026-10-04-1000/`. Priority scope: secrets-engine service JWT login (RPF-WP-0035-T02), informed-decision requester workload delivery (INFD-WP-0005-T05, separate decision), and user-engine transactional mail (USER-WP-0035-T02). The packet records per-owner required receipts, historical custody replay exclusions and the separate no-session RPF-WP-0035-T06 acceptance. Current D1 roots and exact Hub task joins are reviewed in the evidence inventory. No eligible reviewed new execution candidate is claimed; the generated checklist remains NOT READY. T03 stays wait for Custodian review and concrete owner contracts/commands/mappings, rather than the now-supplied date. No schedule or live command is executed. +2026-10-04 continuation: Direct founder date/scope selection supplies the platform +date gate without requiring a repeated Custodian confirmation; its residual source +remains its owner's record. Prepared concrete informed-decision reader artifacts +and silent conflict-refusing metadata child under RPF-WP-0051-T01 (done). The +separate owner-decision checklist now has an explicit proposed CCR/lane/receipt +join, retaining approval/binding/runtime blockers. Corrected mail priority from +credential provisioning to the provider password-setup-link handoff: USER-WP-0035's +2026-09-27 qualification confirms existing authenticated mail. `owner-returns.md` +names the actual three remaining owner acts. T03 depends on those returns rather +than an already-supplied date. No applied requester/mail custody is replayed. + ## Accept platform seam for catalog lane scaffold proposal ```task diff --git a/workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md b/workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md new file mode 100644 index 0000000..a86b217 --- /dev/null +++ b/workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md @@ -0,0 +1,82 @@ +--- +id: RPF-WP-0051 +type: workplan +title: "Prepare and admit informed-decision requester workload delivery" +domain: financials +repo: railiance-platform +status: active +owner: codex +topic_slug: platform-services +created: "2026-10-04" +updated: "2026-10-04" +flavor: implementation +origin: routed +origin_ref: "RPF-WP-0050-T03; INFD-WP-0005-T05; cd811073-d6db-41cf-8a57-10df0b360eab" +--- + +The founder selected existing batched attended custody and delegated relevance +selection for the 2026-10-04 10:00 Europe/Berlin preparation window, then directed +platform to continue. This work owns the new workload reader for existing +requester custody. Existing issuer registration and applied CCR-2026-0026/0027 +are preserved. Private listener, TokenReview transport, runtime OIDC exchange, +submitter admission and live intake rollout remain separately owned. + +## Prepare exact workload reader and owner review bundle + +```task +id: RPF-WP-0051-T01 +status: done +priority: high +``` + +Allocate proposed CCR-2026-0032; render exact-path policy and prospective +Kubernetes-auth role, namespace-restricted ESO, dedicated auth ServiceAccount +and read-only review-pod mount proposal. Keep the bundle outside all active +GitOps rendering. Validate the actual source bundle and record its digests; +preparation supplies no credentials, admitted identity or native receipt. + +2026-10-04: Prepared and validated actual proposed CCR-2026-0032 and generator +input, canonical exact-path policy, prospective auth payload, namespace-restricted +ESO, dedicated auth ServiceAccount and required read-only mount proposal. Added +silent attended metadata child with reviewed-digest/status/binding/envelope gates, +both-object preflight, conflict refusal, missing-only writes and readback. Ten +focused helper tests, fourteen scaffold regressions and seven window regressions +pass; actual bundle checks verify path/namespace/TTL/projection/annotation limits +and proposed-state refusal. Separate INFD-WP-0005-T05 decision checklist maps the +CCR/lane/receipt contract and remains NOT READY. No live action or issuer change. + +## Obtain exact owner runtime and binding acceptance + +```task +id: RPF-WP-0051-T02 +status: wait +priority: high +blocked_on: message-from:informed-decision +blocking_reason: "Owner must accept the proposed informed-decision-eso binding, Secret mount and actual runtime client-credentials exchange; private intake composition and native proof remain." +``` + +Review `docs/credential-lane-designs/informed-decision-requester-workload.md` +against INFD-WP-0005-T05 and its owner dependency contract. Confirm ESO token +creation authority for the exact ServiceAccount. A draft identity is not an +owner acceptance. Retain exact immutable runtime/config references and a bounded +positive/negative/rotation/revocation proof plan before promotion. + +## Apply and accept the scoped workload delivery + +```task +id: RPF-WP-0051-T03 +status: wait +priority: high +depends_on: [RPF-WP-0051-T02] +needs_human: true +decision_id: "0d6792b6-59e4-4fa0-887a-c4e51ea8e131" +blocking_reason: "Founder approval of exact CCR and binding plus attended metadata apply and separately approved ESO/runtime promotion are required after owner acceptance." +``` + +The founder approves the exact new reader, then runs the reviewed contained +metadata child in their terminal. Apply only the new policy/role; do not seed, +rotate or reapply historical requester custody. Promote ESO/runtime through +their actual owner deployment paths after diff/server dry-run and resource +checks. Accept delivery, exact token-policy/identity/TTL, sibling/list denial, +create-only exchange and cleanup/revocation receipts. Revoke this reader on +abort without touching the issuer or historical verifier/attended-reader lanes.