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
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-bindingagainst 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
- 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 freeCCR-YYYY-NNNNfrom the files in the checkout, setsstatus: proposed, clears client-written review comments, validates, commits on branchccr/<id>-<slug>and pushes. Only profilesubmittersmay submit (ops-mason only, founder decision 2026-10-18). A race produces two files with the same id;check-idsin CI refuses the second, which re-submits. Git is the allocator, so no central counter or agent is needed. - 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 underplatform/workloads/<a>/<b>orplatform/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 inproposed|approved|activereading the same path. A pass appends therailiance-platform-ownerreview comment with the profile version and digest. A fail appends aneeds_platform_reviewreview comment with the reasons and, with--notify, sends one hub message torailiance-platform. Either way the CCR status staysproposed; the automated comment never setsapproved. - CI gate. A Forgejo Action on
ccr/*branches runsvalidate,review-standardand the secret scan; only a green branch fast-forwards tomain. The client merges its own green branch. - Founder decision.
submitrecords a State Hub decision for the CCR (templates exist indecision-templates). The founder resolves it; anyone runssync-decisionto move the file toapproved/needs_changes. - Apply and evidence. In-profile CCRs are applied by ops-mason under
ops-mason-construct, or by the applier, after
confirm-binding;record-evidencecloses the CCR toactive. - Platform oversight. Platform owns the profile and reviews changes to it
like code. A weekly
credential-change.py audit-standardlists 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.