railiance-platform/workplans/RPF-WP-0061-self-service-ccr-intake.md
codex 79fbb4bdb5
All checks were successful
CCR intake gate / gate (push) Successful in 1m10s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
CCR-2026-0051: binding confirmed on native evidence, status applied; RPF-WP-0061 follow-up D
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 306568@bnt-lap001
Assistant-Session: 8bdf8150-ea56-41e7-9132-13f04f6b0b91
2026-10-18 12:58:39 +02:00

9.4 KiB

id type title domain repo status owner created updated flavor origin origin_ref state_hub_workstream_id
RPF-WP-0061 workplan Self-service CCR intake: clients file and progress CCRs without a platform worker financials railiance-platform active codex 2026-10-18 2026-10-18 implementation cross-repo ops-mason daa119e6 / 049afa1f; MASON-IN-0005 bad0cc9b-e8ad-572b-8d0d-fb3adb208b4a

Demand: every credential change request today waits on a railiance-platform worker session to allocate the CCR number, copy the client's draft and policy file into this repo, run credential-change.py validate, write the platform owner review and relay the founder command. For routine lanes the worker adds no judgement: ops-mason already produces drafts that pass validate (MASON-WP-0010-T03, MASON-WP-0014), and from MASON-IN-0005 ops-mason can apply workload-kv-read-* policies and external-secrets-* roles itself. The worker has become a relay, and clients stall whenever no platform session runs.

Goal: a client can take a standard CCR from draft to approved and applied using only repo tooling, CI and the State Hub decision flow. Platform judgement is moved into a versioned, reviewed standard lane profile; only requests that fall outside it are routed to a platform worker.

Invariants (unchanged by this plan):

  • CCRs remain non-secret repo files in credential-change-requests/; the repo stays authoritative and the hub stays a read model.
  • Founder approval is never automated. Bound claims still need confirm-binding against native state before apply.
  • Nothing here widens ops-mason-construct or any applier grant; secret-id, token creation and value reads stay with the founder and applier.

Design

  1. Submit. The client runs, in a railiance-platform checkout, scripts/credential-change.py submit --ccr <draft> --policy <hcl> --slug <slug> --requester <agent> --push. It copies the CCR and policy file, allocates the next free CCR-YYYY-NNNN from the files in the checkout, sets status: proposed, clears client-written review comments, validates, commits on branch ccr/<id>-<slug> and pushes. Only profile submitters may submit (ops-mason only, founder decision 2026-10-18). A race produces two files with the same id; check-ids in CI refuses the second, which re-submits. Git is the allocator, so no central counter or agent is needed.
  2. Platform review as policy-as-code. credential-change.py review-standard <ccr> checks the standard lane profile (registry/ccr-standard-profile.yaml): workload-kv-read; kubernetes auth; exactly one KV path under platform/workloads/<a>/<b> or platform/suppliers/<supplier>/<account>; policy file byte-equal to the generated HCL (read on the data path plus lookup/revoke-self only); ttl and max ttl ≤ 15m; token_no_default_policy: true; one SA in one namespace; no other CCR in proposed|approved|active reading the same path. A pass appends the railiance-platform-owner review comment with the profile version and digest. A fail appends a needs_platform_review review comment with the reasons and, with --notify, sends one hub message to railiance-platform. Either way the CCR status stays proposed; the automated comment never sets approved.
  3. CI gate. A Forgejo Action on ccr/* branches runs validate, review-standard and the secret scan; only a green branch fast-forwards to main. The client merges its own green branch.
  4. Founder decision. submit records a State Hub decision for the CCR (templates exist in decision-templates). The founder resolves it; anyone runs sync-decision to move the file to approved/needs_changes.
  5. Apply and evidence. In-profile CCRs are applied by ops-mason under ops-mason-construct, or by the applier, after confirm-binding; record-evidence closes the CCR to active.
  6. Platform oversight. Platform owns the profile and reviews changes to it like code. A weekly credential-change.py audit-standard lists every auto-reviewed CCR so a platform session can spot-check after the fact.

T01 — Define the standard lane profile

id: RPF-WP-0061-T01
status: done
priority: high
state_hub_task_id: "c18752a6-6126-5410-bbb6-1799ccd63558"

Write registry/ccr-standard-profile.yaml from the existing approved ESO lanes (CCR-2026-0033, -0035, -0041, -0050, -0051 shapes). Record which past CCRs would and would not have passed it.

Done 2026-10-18: registry/ccr-standard-profile.yaml v1. Calibrated on all 51 CCRs: only CCR-2026-0051 passes; the profile comment lists the first failing rule for every other CCR. Stricter than past practice by design.

T02 — submit and review-standard subcommands

id: RPF-WP-0061-T02
status: done
priority: high
state_hub_task_id: "5dd5305f-e1e4-52f5-88aa-f0090e072d0f"

Add both subcommands to scripts/credential-change.py with tests covering id allocation, duplicate-path refusal, policy-file mismatch and every profile rule. review-standard never touches OpenBao.

Done 2026-10-18: scripts/ccr_intake.py adds submit, review-standard (--check, --notify) and check-ids to credential-change.py; tests/test_ccr_intake.py has 20 tests against a scratch repo root. Two existing failures in tests/test_credential_change.py (CCR-2026-0035 schema gaps, core-hub database policy assertion) predate this work.

T03 — CI gate on ccr/* branches

id: RPF-WP-0061-T03
status: done
priority: medium
state_hub_task_id: "0fd97924-1bc7-5aa1-a520-c6c5737bf09b"

Forgejo Action running validate, review-standard and the secret scan; confirm which identities may push ccr/* (ops-mason first).

2026-10-18: .forgejo/workflows/ccr-intake.yaml runs on ccr/** and main from exact-revision archives. On ccr/* it runs the intake tests and credential-change.py check-branch. That command refuses the branch unless it adds exactly one new CCR plus its new policy file and changes nothing else (not the profile, not an existing policy). The CCR must validate, be proposed, come from a listed submitter, re-pass the profile in CI and carry exactly the policy-as-code approval for the current profile digest. On main it runs check-ids. 26 tests. CCR-2026-0051 received the first automated platform-owner review (still proposed).

Remaining, founder/Forgejo admin: a protected-branch rule on ccr/* that allows pushes only from ops-mason's Forgejo identity. Forgejo cannot require a check on main without blocking every direct push, so merging is a convention backed by detection: ops-mason fast-forwards only a green branch, and the main job plus check-branch evidence detect violations after the fact.

T04 — Hub decision on submit and client-run sync-decision

id: RPF-WP-0061-T04
status: done
priority: medium
state_hub_task_id: "15ac9eae-9890-5140-b1ca-cd0ad3ca77ac"

2026-10-18: credential-change.py request-decision <ccr> opens the founder decision for a proposed CCR whose latest review is a platform-owner approval, refuses a second decision or a decision returned already resolved, and writes state_hub.decision_id into the CCR. The existing sync-decision moves the file. First use: CCR-2026-0051, decision e633607e. The CI gate passed on main at 3d97117 after adding the jsonschema pin.

Open, needs a founder choice: the hub does not authenticate decisions. decided_by on resolve is free text, and POST /decisions/ accepts an already-resolved decision. With self-service, the requester could resolve its own CCR decision and run sync-decision. This gap existed before (the platform worker never verified the resolver) but self-service makes it cheaper to exploit. Until it is closed, sync-decision on a self-service CCR stays a platform/founder step.

Founder decision 2026-10-18: option A. Clients submit, pass CI, get the automated review and open the decision themselves; the founder resolves it; sync-decision stays with a platform session or the founder. CCR-2026-0051 went through this path: founder resolved e633607e at 10:46:17Z; synced at 45c8d93. Follow-ups, not part of this plan: (B) founder approval provable through an MFA-backed OIDC login so clients can sync themselves; (C) separate Forgejo and OS identities per agent so ccr/* can be restricted. Both stem from the finding that every workstation agent pushes as Forgejo user tegwick with the key workstation-automation. (D, ops-mason point 5) Self-service teardown: deactivate lifecycle event filed by the client and applied under ops-mason-construct. Founder to decide whether a deactivate needs a decision. Until then teardown goes through a platform session. ops-mason points 1-4 were adopted in 7e7ca8b (plan_ref, render-policy, single hub decision, readback before record-evidence).

T05 — Client documentation and first self-service CCR

id: RPF-WP-0061-T05
status: wait
priority: medium
state_hub_task_id: "27797fa5-3d47-5037-a98c-2d269198d1c4"

Document the flow in docs/credential-change-approval.md and notify ops-mason and ops-warden. Acceptance (narrowed by option A): ops-mason submits one CCR, passes the CI gate, merges and opens the decision with no railiance-platform worker session; only the post-approval sync is platform/founder.

2026-10-18: guide added as "Self-Service Intake" in docs/credential-change-approval.md; ops-mason and ops-warden notified. Waiting for ops-mason's first self-service submission, which also gives the first live ccr/* CI run.