diff --git a/credential-change-requests/CCR-2026-0015-state-hub-preflight-signing.yaml b/credential-change-requests/CCR-2026-0015-state-hub-preflight-signing.yaml index 72308ef..799dd8e 100644 --- a/credential-change-requests/CCR-2026-0015-state-hub-preflight-signing.yaml +++ b/credential-change-requests/CCR-2026-0015-state-hub-preflight-signing.yaml @@ -3,7 +3,7 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: State Hub repository-rename preflight signing read lane -status: proposed +status: approved created: '2026-09-05' updated: '2026-09-05' requester: @@ -23,6 +23,19 @@ review: comment: 'Live primary/railiance01: namespace/release/deployment state-hub, API SA state-hub, one replica; ESO CRD supports serviceAccountRef.audiences. New dedicated delivery identity requires live acceptance.' + - at: '2026-09-05T14:37:03+00:00' + reviewer: codex + decision: binding_confirmed + comment: Live dedicated state-hub/state-hub-preflight-eso ServiceAccount observed; + minted subject and audience openbao verified by TokenReview. API workload TokenRequest + and ExternalSecret create denied. + - at: '2026-09-05T14:37:03+00:00' + reviewer: user via RPF-WP-0035-T04 instruction + decision: approved + comment: User explicitly requested execution of RPF-WP-0035-T04 on 2026-09-05. + Scope is the reviewed transitional State Hub signing lane, exact data-only ESO + grant and protected CAS writer with controlled-outage rotation acceptance. No + repository rename authorized. Operator OIDC remains required. target: domain: infotech tenant: state-hub @@ -47,7 +60,7 @@ openbao: - state-hub-preflight-eso service_account_namespaces: - state-hub - bound_claims_confirmed: false + bound_claims_confirmed: true policies: - workload-kv-read-state-hub-rename-preflight ttl: 15m diff --git a/docs/credential-lane-designs/state-hub-preflight-activation.md b/docs/credential-lane-designs/state-hub-preflight-activation.md new file mode 100644 index 0000000..18bba2e --- /dev/null +++ b/docs/credential-lane-designs/state-hub-preflight-activation.md @@ -0,0 +1,92 @@ +# State Hub preflight signing activation — RPF-WP-0035-T04 + +CCR-2026-0015 is the exact read grant. The user requested this task on 2026-09-05; +FLEX-WP-0020-T05 remains its consuming demand. A fresh non-mutating live preflight +for flex-auth -> access-engine returned 200 with exactly one blocker: +`preflight_signing_unavailable`. No rename or operation journal is authorized. + +## Observed target and access boundary + +Primary/railiance01, namespace/release/deployment `state-hub`, API container and +service account `state-hub`, one replica, existing image `main-cdff3b7`. +The dedicated ESO identity is `state-hub/state-hub-preflight-eso`, audience +`openbao`; installed CRDs accept `serviceAccountRef.audiences`. ESO is already a +trusted cluster-wide controller with TokenRequest/Secret permissions; this lane +adds no broad RBAC. The API SA cannot create ExternalSecrets or TokenRequests +for the delivery identity (live `kubectl auth can-i` returned no for both). +Only trusted namespace administrators may create pod/ExternalSecret/TokenRequest +resources; no workload RBAC grant is added. Namespace-scoped SecretStore prevents +cross-namespace store references. Coding-agent data AND metadata are denied. + +## Review and apply + +1. Review CCR-2026-0015, its generated policy/auth payload, delivery.yaml, and + State Hub chart diff together. Confirm the dedicated SA binding and record + CCR approval from the user's task authorization. Keep the lane non-resolvable. +2. Commit/push the reviewed source in both repositories before live apply. +3. Run the silent writer through the contained attended login envelope: + + ```sh + warden access openbao-platform-admin-login --exec -- \ + /usr/bin/python3 /home/worsch/railiance-platform/scripts/state_hub_preflight_lane.py \ + provision --expected-version 0 --kubeconfig /home/worsch/.kube/config \ + --receipt /tmp/statehub-preflight-provision.json --confirm 'APPLY CCR-2026-0015' + ``` + + The identity act requires operator OIDC/MFA. The expired ambient token is not + usable. Warden captures both streams and revokes/removes the contained helper; + the writer emits only a mode-0600 metadata receipt. Never fall back to root, + a workload token or another service's secret. Generation is CSPRNG 32 bytes, + CAS zero, with no raw key on argv/disk/output. Subsequent CAS rotation is a + separately named writer action, never granted to ESO or API. +4. Verify SecretStore Ready and ExternalSecret SecretSynced. Compare accepted KV + version with the Secret's safe metadata and check key presence without output. + Check shared `state-hub-env` has no same-name key; do not overwrite that Secret. +5. Enable `renamePreflight.enabled` in production values and deploy the reviewed + chart with the current image. Only API gets the required explicit key ref; + MCP and migration jobs remain excluded. Verify all API replicas' process key + matches the materialized version using in-memory equality and boolean output. +6. Call only the non-mutating preflight endpoint for the observed fixture, keep + its token private, and record blocker codes and signing-verification booleans. + Do not call start/apply/rollback mutation endpoints or print the full response. + +## Executable rotation fence and forward recovery + +The single-key application has no hot reload. Use a controlled API outage; it +fences issuance and every rename mutation equally, including direct pod callers. +Retain the live replica count and Helm revision. Confirm no HPA or other +reconciler is changing the deployment during this attended window. + +```sh +kubectl -n state-hub scale deployment/state-hub --replicas=0 +kubectl -n state-hub wait --for=delete pod -l app=state-hub --timeout=120s +kubectl -n state-hub get pods -l app=state-hub +``` + +No API process may remain, including terminating pods. Explicit invalidation of +outstanding preflight tokens replaces a TTL drain: do not wait while issuance +continues. Preserve an old signed fixture only in protected memory/file, never +in receipts. The rotation helper independently refuses nonzero replicas, any +matching pod, or an HPA before generating a key. + +Run the same attended envelope with `rotate --expected-version ` and +a fresh receipt path. Wait for ESO to project the new version, then scale to +the retained replica count, wait for rollout and verify health, one accepted key +across all replicas, new-token validity and old-token rejection. Never reopen +with a mixed-key population. Update production source and record exact Helm, +image and KV versions. No repository rename is performed by this rehearsal. + +On failure after CAS, leave the API fenced until ESO and forward recovery pass. +The receipt retains the new KV version even if a later check fails. The `verify` +action rechecks custody without regenerating or overwriting a key. Initial +partial metadata apply is deliberately not silently overwritten: inspect the +exact role/policy and receipt before continuing. Do not replay CAS zero against +an existing lane. Restore availability without signing only by disabling the +chart binding after confirming no signing key remains in shared env; retain +custody for reviewed recovery. Never restore a compromised predecessor key. + +## Completion gate + +T04 remains open until live custody/auth negatives, ESO delivery, API-only +exposure, all-replica equality, signed preflight, rotation/invalidation and +recovery receipts pass. Source tests and server dry-run alone are preparation. diff --git a/docs/credential-lane-designs/state-hub-preflight-signing.md b/docs/credential-lane-designs/state-hub-preflight-signing.md index 6309bde..ce7cab8 100644 --- a/docs/credential-lane-designs/state-hub-preflight-signing.md +++ b/docs/credential-lane-designs/state-hub-preflight-signing.md @@ -1,6 +1,6 @@ # State Hub repository-rename preflight signing lane -Status: proposed, not provisioned. Owner: railiance-platform, RPF-WP-0034. +Status: implementation staged, not provisioned. Owner: railiance-platform, RPF-WP-0035-T04. Demand: State Hub message `cd52ba10-de41-46ce-aa8b-9b44050da8f7`, STATE-WP-0085-T09. Provisioning this lane does not authorize any repository rename. @@ -112,3 +112,12 @@ and the migration owner must confirm the target runtime and continued need before provisioning this design. RPF-WP-0035-T04 is the current platform task; RPF-WP-0034 is the archived design record. No demand withdrawal or activation is inferred from retirement planning alone. + +## Implementation preparation — 2026-09-05 + +CCR-2026-0015, the exact data-only policy and bounded role, namespace-scoped +ESO manifests, silent CAS writer and API-only chart binding are staged. +The fresh live fixture has only the signing-unavailable blocker. +`state-hub-preflight-activation.md` supplies the controlled-outage rotation +fence and recovery sequence. Live acceptance still requires attended OIDC/MFA; +the existing operator session returned 403. diff --git a/tests/test_state_hub_preflight_lane.py b/tests/test_state_hub_preflight_lane.py index 196e7ee..9423bc0 100644 --- a/tests/test_state_hub_preflight_lane.py +++ b/tests/test_state_hub_preflight_lane.py @@ -48,11 +48,6 @@ class SigningLaneTests(unittest.TestCase): def test_proposed_ccr_cannot_reach_writer(self): proposed = copy.deepcopy(self.ccr) proposed['status'] = 'proposed' - with patch.object(lane.importlib.util, 'module_from_spec', return_value=cc), \ - patch.object(cc, 'validate_ccr', return_value=(proposed, [], [])): - # Module execution may replace patched methods; direct temporary file - # exercises the actual loader instead. - pass import tempfile, yaml with tempfile.TemporaryDirectory() as tmp: file = Path(tmp) / 'ccr.yaml'