---
id: RPF-WP-0024
type: workplan
title: "Credential Change Proposal Review Workflow"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 7
created: "2026-06-27"
updated: "2026-06-30"
depends_on_workplans:
  - RAIL-PL-WP-0002
  - RAILIANCE-WP-0005
  - RAILIANCE-WP-0006
state_hub_workstream_id: "6c2e61cf-7aa9-58ee-bb22-3514b9692eb5"
---

> **Terminology note:** Historical text in this archived workplan may use the legacy term "workstream". The fleet term is **workplan** (`canon/standards/workplan-terminology-fleet_v0.1.md`).

# RPF-WP-0024 - Credential Change Proposal Review Workflow

## Goal

Create a proposal -> review -> approve/deny with comment -> apply -> verify
workflow for credential and it-sec changes, so operators do not need to author
or mentally validate raw OpenBao commands.

The first target is the whynot-design npm token lane from `RAILIANCE-WP-0006`.
The workflow should then generalize to workload KV paths, OpenBao token roles,
ops-warden access catalog entries, External Secrets lanes, credential rotation,
deactivation, and compromise handling.

## Direction

Do not start by extending OpenBao. Instead, build a small approval control
plane around OpenBao:

- OpenBao remains the enforcement, secret storage, token, and audit engine.
- State Hub stores non-secret request lifecycle, comments, decisions, and
  evidence.
- Repo files store reviewable non-secret request specs and generated policy
  artifacts.
- Agents and CLIs create proposals and render them for human review.
- Humans approve or deny with comments.
- Only approved requests can be applied by an operator-controlled runner or
  interactive runbook.

If the workflow proves valuable, a later UI or OpenBao extension can surface the
same request index and statuses.

## Proposed Object

Introduce a non-secret Credential Change Request, or `CCR`.

Each CCR captures:

- request id, title, requester, reviewer, approver, and applier;
- target tenant/workload/environment/purpose;
- OpenBao mount, path, fields, policies, auth roles, and bound claims;
- access front door such as ops-warden, External Secrets, CSI, or direct caller
  fetch;
- risk classification and approval requirements;
- generated apply plan and verification plan;
- rollback, deactivate, rotate, and compromise response plan;
- comments, decision, timestamps, and non-secret audit evidence.

Each CCR explicitly excludes secret values, token values, private keys,
passwords, unseal/recovery material, and secret-bearing command output.

## Tasks

## T01 - Record the approval workflow design

```task
id: RPF-WP-0024-T01
status: done
priority: high
state_hub_task_id: "045e5d30-1dc6-5a2e-be93-b8d2311f6c3d"
```

Document the desired operator workflow and why it should sit around OpenBao
rather than inside the OpenBao UI initially.

Acceptance:

- The design describes the proposal, review, approval/denial, apply, verify,
  activate, deactivate, rotate, and compromised states.
- The design names where State Hub, OpenBao, ops-warden, repo files, agents,
  and interactive runbooks fit.
- The design keeps secret values out of State Hub, Git, chat, and prompts.

**2026-06-27:** Added `docs/credential-change-approval.md` with the control
plane direction, CCR object, state machine, State Hub/OpenBao/ops-warden roles,
interactive runbook role, and compromise/deactivation path.

## T02 - Define the CCR schema and storage layout

```task
id: RPF-WP-0024-T02
status: done
priority: high
state_hub_task_id: "8ec9d4c5-b832-5a9e-accb-8de589cd9d80"
```

Create a versioned non-secret schema for credential change requests.

Acceptance:

- A schema exists for `workload-kv-read` requests covering mount, path, fields,
  policy name, auth role, bound claims, access front door, verification plan,
  and activation conditions.
- The schema supports decision metadata: requested, proposed, approved,
  denied, needs_changes, applied, verified, active, deactivated, rotated,
  compromised, superseded, and cancelled.
- The schema supports comments and references State Hub ids without storing
  secrets.
- Example CCR fixtures include the whynot-design npm token lane.

**2026-06-27:** Added `schemas/credential-change-request.schema.yaml`, the
`credential-change-requests/` storage directory, and
`credential-change-requests/CCR-2026-0001-whynot-design-npm-publish.yaml` as the
first non-secret CCR fixture. The whynot CCR is intentionally `proposed` and
marks the bound claim as unconfirmed, so apply is blocked until review.

## T03 - Add offline validation and rendering

```task
id: RPF-WP-0024-T03
status: done
priority: high
state_hub_task_id: "9d1cabba-8d37-5212-9cc9-69e9398fbfa3"
```

Add a helper that validates CCR files and renders human review summaries.

Acceptance:

- Invalid CCRs fail before any OpenBao apply is attempted.
- The renderer produces a compact review block that a human can understand in
  chat or State Hub.
- The renderer highlights risky fields: broad claims, wildcard paths,
  privileged policies, missing negative verification, and missing deactivation
  plan.
- A secret-pattern scan rejects likely token values in CCR files.

**2026-06-27:** Added `scripts/credential-change.py validate` and `render`,
plus Make targets `credential-change-validate` and `credential-change-render`.
Validation rejects secret-looking markers and broad/unsafe request shapes; render
produces the chat/State Hub review summary and highlights unconfirmed bound
claims. CCRs now also carry machine-readable front-door readiness fields:
`access_frontdoor.readiness` and `access_frontdoor.resolvable`. Unit coverage
lives in `tests/test_credential_change.py`.

## T04 - Generate OpenBao apply plans from approved CCRs

```task
id: RPF-WP-0024-T04
status: done
priority: high
state_hub_task_id: "7f827d6c-86c1-5d85-ac6a-6ef850636c93"
```

Generate deterministic, reviewable OpenBao apply plans from CCRs.

Acceptance:

- A workload KV CCR can generate policy HCL and auth-role commands or API
  payloads.
- The plan includes a dry-run mode and a diff against existing source
  artifacts when available.
- Applying a plan is refused unless the CCR is approved.
- The applier uses an approved operator authority path and does not accept raw
  tokens in argv or logs.

**2026-06-27:** Added `plan` and guarded `apply-plan` rendering for workload KV
CCRs, with Make targets `credential-change-plan` and
`credential-change-apply-plan`. `apply-plan` currently refuses any CCR that is
not `approved` and also refuses unconfirmed bound claims. Remaining T04 work is
to add a richer diff against existing source artifacts and eventually bridge
from reviewed plan to the interactive live applier.

**2026-06-28:** Added OIDC `allowed_redirect_uris` to the CCR contract and
generated role payloads after live OpenBao rejected an OIDC role without
callbacks. Unit coverage now checks the generated whynot-design role payload.

**2026-06-30:** Added source-artifact diff rendering to `plan` and delegated
`applier-dry-run` output. The generated plan now reports whether the checked-in
policy artifact matches the CCR-generated HCL and shows a unified diff when it
does not. Approved-only `apply-plan`/`operator-commands` remain gated by CCR
status and confirmed auth binding.

## T05 - Add chat/CLI approval commands

```task
id: RPF-WP-0024-T05
status: done
priority: high
state_hub_task_id: "41d76d6f-0c46-5792-a86e-dcca45004448"
```

Make the workflow usable from chat and command line.

Acceptance:

- Operators can approve, deny, or request changes with a comment.
- Approvals/denials are recorded as non-secret State Hub events and in the CCR
  file or linked decision record.
- The system refuses apply when the latest human decision is denied or
  needs_changes.
- Agents can propose changes and respond to review comments without receiving
  secret values.

**2026-06-27:** Added file-backed `approve`, `deny`, and `needs-changes`
commands that require reviewer and comment text and append non-secret review
comments to the CCR. Added `confirm-binding` for explicit non-secret auth
binding confirmation. Added `status` plus Make targets
`credential-change-status` and `credential-change-status-json` so ops-warden can
consume `readiness`/`resolvable` without scraping prose. Remaining T05 work is
State Hub decision-event emission and tighter chat integration. Created a
State Hub decision for `CCR-2026-0001` and added `sync-decision` so resolved
State Hub decisions can update the file-backed CCR status.

**2026-06-30:** Added optional `--record-state-hub` emission for approve, deny,
and needs-changes commands. Review comments are checked for known secret markers
before being written, and the State Hub progress event records only non-secret
CCR id/path/policy/field/auth-role metadata plus the reviewer comment.

## T06 - Build an interactive runbook for apply and verify

```task
id: RPF-WP-0024-T06
status: done
priority: high
state_hub_task_id: "d406e99b-31a3-5446-aa91-b19889698c2b"
```

Wrap privileged application in an operator-friendly guided runbook.

Acceptance:

- The runbook loads an approved CCR, shows the plan, asks for final attended
  confirmation, then applies policy/auth metadata.
- Secret value entry is handled through an approved OpenBao/operator path and
  is never echoed or logged.
- Positive and negative verification steps are guided.
- Non-secret evidence is recorded automatically.

**2026-06-30:** Added `scripts/credential-change.py runbook <CCR>` and Make
target `credential-change-runbook` to render the attended operator checklist,
final confirmation phrase, metadata apply guidance, secret custody instructions,
positive/negative verification steps, activation conditions, and evidence
commands. `runbook --execute-metadata` is opt-in, requires the exact `APPLY
<CCR-ID>` confirmation phrase, uses the local `bao` CLI with ambient approved
operator authority, writes only policy/auth metadata, and records a non-secret
`metadata_apply` evidence entry. Added `record-evidence` plus Make target
`credential-change-record-evidence` so operators can append apply, secret
provisioning, verification, and activation evidence to the CCR and optionally
State Hub without storing secret values.

## T07 - Pilot with whynot-design and ops-warden

```task
id: RPF-WP-0024-T07
status: done
priority: high
state_hub_task_id: "ab537d3f-87b0-5934-9da8-46400a232faf"
```

Use the existing whynot-design npm token lane as the first end-to-end pilot.

Acceptance:

- The current whynot-design lane is represented as a CCR.
- The CCR is rendered and reviewed in chat or State Hub.
- A human approval or denial comment is recorded.
- If approved, the runbook applies the policy/auth metadata, guides secret
  provisioning, verifies access, and notifies ops-warden.
- ops-warden activates its catalog entry only after CCR verification.

**2026-06-27:** The whynot-design lane is represented as `CCR-2026-0001` and
can be rendered for review. The whynot-design bound claim was confirmed from
operator chat context and recorded in the CCR, but it remains proposed/unapproved,
so live apply and ops-warden activation are correctly blocked.

**2026-06-27:** Converted the ops-warden batch follow-up
`fe5b1696-8956-4bd5-9d6f-dbde1901a076` into three proposed CCRs:
`CCR-2026-0001` for `whynot-design-npm-publish`, `CCR-2026-0002` for
`issue-core-ingestion-api-key`, and `CCR-2026-0003` for
`llm-connect-openrouter-api-key`. All three are explicitly `readiness: template`
and `resolvable: false` until owner confirmation, approval, OpenBao apply,
secret provisioning, and verification are complete.

**2026-06-28:** Synced State Hub decision
`250669d0-8475-4527-9624-cd072249f9a9` into `CCR-2026-0001`; the lane is now
`approved` with confirmed binding and `apply_allowed: true`. A live OpenBao
policy apply using the available token helper reached the active OpenBao pod but
still failed with `403 permission denied` on
`sys/policies/acl/workload-kv-read-whynot-design-npm-publish`, so the front door
remains `readiness: template` and `resolvable: false`. Added guarded
`credential-change-operator-commands` output so a platform operator can run the
reviewed non-secret policy and auth-role commands without hand-writing them;
secret value provisioning and verification remain under approved custody.

**2026-06-28:** After correcting the tenant/org to `coulomb`, the corrected
approval was synced from State Hub decision
`e6381a56-6b04-4fd5-b2de-f3ef59cde888`; `CCR-2026-0001` is approved and
`apply_allowed: true` for
`platform/workloads/coulomb/whynot-design/npm-publish`. The operator reported
secret provisioning likely completed, but Codex metadata-only verification still
received `403 permission denied`. Prepared
`docs/whynot-design-npm-publish-handoff.md` as the next-session checklist for
policy, auth-role, metadata verification, positive verification, negative
verification, and activation without printing the token.

**2026-06-28:** With the temporary operator token, Codex applied/confirmed the
OpenBao read policy and OIDC role, confirmed metadata `catalog-id`, and confirmed
`NPM_AUTH_TOKEN` field presence without printing or recording the value. The CCR
now records non-secret evidence for that apply check. Positive whynot-design and
negative non-whynot caller verification still gate `active`/`ready`.

**2026-06-29:** The whynot-design pilot completed OpenBao verification. Positive
fetch succeeded with output suppressed, negative login failed with the expected
groups bound-claim mismatch, `platform-root` membership was restored afterward,
and `CCR-2026-0001` is now active/ready/resolvable. ops-warden catalog
confirmation remains the external closeout step.

**2026-06-30:** Closed the pilot task based on the active/ready/resolvable CCR
state and prior ops-warden catalog confirmation that the selector is active and
resolvable. The remaining lifecycle work is now tracked separately in T08.

## T08 - Add deactivation, rotation, and compromise flows

```task
id: RPF-WP-0024-T08
status: done
priority: medium
state_hub_task_id: "67f26581-7748-5d32-a280-4b0eeca01864"
```

Support lifecycle states beyond initial creation.

Acceptance:

- Existing credentials can be imported as CCR-backed inventory without secret
  values.
- Operators can mark a lane deactivated, rotated, or compromised with reason
  and evidence.
- Deactivation disables the relevant access front door and auth/policy path.
- Compromise flow records blast-radius notes and required follow-up tasks.

**2026-06-30:** Added `lifecycle-plan`, `lifecycle-event`, and
`import-inventory` commands plus Make targets. Lifecycle plans render
deactivation, rotation, and compromise guidance, including access-front-door
state changes and OpenBao metadata disable commands for deactivation or
compromise. Lifecycle events update CCR status/front-door readiness, append
non-secret lifecycle evidence, and optionally post State Hub progress.
Compromise events accept non-secret blast-radius and follow-up references.
`import-inventory` can create a CCR-backed inventory file and matching read
policy artifact for an existing lane without asking for or storing secret
values.

## T09 - Add decision templates and guided review actions

```task
id: RPF-WP-0024-T09
status: done
priority: high
state_hub_task_id: "b0d96db2-f66e-5786-ba73-3f47a26c010f"
```

Remove the current friction where reviewers must know magic rationale prefixes
for State Hub decisions to sync back into CCR status.

Acceptance:

- Each CCR review page or chat handoff shows explicit approve, deny, and needs
  changes templates.
- Generated templates include the accepted prefixes (`APPROVE:`, `DENY:`, and
  `NEEDS_CHANGES:`) and pre-fill the CCR id, corrected path, policy, auth role,
  and non-secret rationale prompt.
- The dashboard or agent response links directly to the decision and states what
  phrase or button will be recognized.
- The sync tooling refuses ambiguous free-text approvals with a friendly message
  that shows the valid templates.
- Future UI work can replace prefix parsing with structured decision outcomes
  without changing the CCR audit trail.

**2026-06-30:** Added `scripts/credential-change.py decision-templates <CCR>`
and Make target `credential-change-decision-templates`. The generated templates
include accepted prefixes, CCR id, KV path, policy, auth-role path, and the
linked State Hub decision. Ambiguous State Hub rationale text now fails with the
valid templates in the error message.

## Exit Criteria

- A human can review and approve or deny a credential/security change without
  writing raw OpenBao commands.
- An approved request can be applied by an operator-controlled helper or
  interactive runbook.
- State Hub and repo artifacts contain non-secret lifecycle, decision, and
  evidence records.
- OpenBao remains the enforcement and audit source for actual secret access.
- The whynot-design npm token lane can complete through this workflow.
