--- id: RAILIANCE-WP-0005 type: workplan title: "Credential Request and Lease Broker" domain: financials repo: railiance-platform status: blocked owner: codex topic_slug: railiance planning_priority: high planning_order: 5 created: "2026-06-24" updated: "2026-06-27" depends_on_workplans: - RAIL-PL-WP-0002 state_hub_workstream_id: "2731fece-6c49-45b8-ab8a-4ea6c04ac603" --- # RAILIANCE-WP-0005 - Credential Request and Lease Broker ## Goal Provide a clean, secure, low-friction way for operators, agents, and approved automations to request, generate, receive, use, renew, and revoke short-lived credentials such as the OpenBao token needed for ops-warden vault-backed SSH signing smoke. The target experience is self-service for routine, policy-approved leases and explicit human approval for high-risk grants, without ever pasting secret values into Git, State Hub, chat, prompts, workplans, or shell history. ## Repository Decision The primary owner is railiance-platform because OpenBao is the canonical runtime secret custody service and this repo owns platform secrets, identity integration, and shared credential delivery contracts. Cross-repo responsibilities: | Concern | Owner | Boundary | | --- | --- | --- | | OpenBao policies, token roles, lease broker, audit | railiance-platform | Owns secret custody and credential generation. | | Login, OIDC, MFA, IAM profile claims | key-cape | Authenticates humans and service identities. | | Authorization decision for requested grants | flex-auth | May decide whether actor X may request grant Y for purpose Z. | | SSH certificate signing | ops-warden | Issues SSH certs only; does not vend OpenBao tokens. | | Request tracking and progress | state-hub | Stores non-secret request metadata, status, decision ids, and audit pointers only. | | Agent inference/runtime | llm-connect and callers | Never place secrets in prompts; consume via local env injection or wrapped lease handles. | This work should update the ops-warden routing catalog when complete, but the implementation belongs here. If the broker later becomes a general NetKingdom service, code can split to a dedicated credential-broker repo while OpenBao policies and grants remain owned by railiance-platform. ## Design Principles - Prefer dynamic or short-lived leases over static secrets. - Use response wrapping or local exec-time injection; do not print raw tokens by default. - Store non-secret lease metadata only: actor, grant, TTL, purpose, lease id or accessor, decision id, timestamps, and revocation state. - Keep OpenBao audit logs as the source of truth for secret access. - Make the common path easy: one command to run a task with the right credential. - Keep high-risk paths explicit: human approval and MFA for elevated grants. - Every grant has a catalog entry, max TTL, allowed actors/subjects, delivery mode, audit expectations, and revocation behavior. ## Proposed User Experience Initial pilot command shapes: credential request vault-token --grant ops-warden/warden-sign --purpose flex-auth-openbao-smoke --ttl 15m credential exec --grant ops-warden/warden-sign --ttl 15m -- SMOKE_VAULT=1 /home/worsch/ops-warden/scripts/policy_gate_production_smoke.sh credential status credential revoke For the ops-warden smoke, the preferred path is credential exec. It obtains a bounded OpenBao token with the warden-sign policy, injects it as VAULT_TOKEN only into the child process environment, redacts logs, and revokes or lets the lease expire after the command finishes. ## Threat Model Summary Primary risks: - token leakage through shell history, logs, prompts, chat, State Hub, or Git; - confused-deputy issuance where an agent requests a broader token than needed; - stale leases surviving after a task completes; - bypassing KeyCape identity or flex-auth authorization checks; - replacing one manual secret-handling ritual with another brittle one. Mitigations required by this workplan: - no raw token in command-line arguments, State Hub payloads, workplans, or logs; - bounded OpenBao token roles and policies; - response wrapping for copy/paste or remote handoff flows; - exec-time environment injection for local command execution; - default TTLs measured in minutes, with explicit max TTLs per grant; - revocation by lease handle/accessor; - OpenBao audit verification and non-secret State Hub progress events. ## Tasks ## T01 - Record ownership and architecture decision ```task id: RAILIANCE-WP-0005-T01 status: done priority: high state_hub_task_id: "cd680de8-a483-40d6-84fa-369bad60e7c7" ``` Write an ADR or docs section confirming railiance-platform as the owner for OpenBao credential request/generation/delivery, with key-cape, flex-auth, ops-warden, state-hub, and llm-connect boundaries. Acceptance: - Docs state that ops-warden routes SSH certs only and must not vend OpenBao tokens. - Docs state that State Hub stores request metadata only, never secret values. - Ops-warden credential routing can point OpenBao token requests here. **2026-06-25:** Added `docs/credential-broker.md` as the ownership and architecture decision. It records that railiance-platform owns OpenBao credential request/generation/delivery, ops-warden owns SSH certificate signing only, State Hub stores non-secret request metadata only, and llm-connect/callers must not place secrets in prompts. ## T02 - Define credential grant catalog ```task id: RAILIANCE-WP-0005-T02 status: done priority: high state_hub_task_id: "6b64ad4b-90cd-475b-aaa9-73997c6b011b" ``` Add a non-secret grant catalog schema and initial grant entries. Initial grant: - id: ops-warden/warden-sign - credential type: openbao-token - policies: warden-sign - default TTL: 15 minutes - max TTL: 1 hour unless a human approves more - purpose examples: flex-auth OpenBao smoke, ops-warden production sign smoke - allowed delivery: exec-env, response-wrap, local-token-file mode 0600 - denied delivery: chat, State Hub body, Git, command-line token argument Acceptance: - Catalog can be validated in CI. - The catalog distinguishes self-service, approval-required, and break-glass grants. - No grant entry contains a secret. **2026-06-25:** Added the non-secret grant catalog at `credential-grants/catalog.yaml` with the initial `ops-warden/warden-sign` pilot grant, plus `scripts/credential-grants-validate.py` and `make credential-grants-validate`. The validator enforces required fields, TTL bounds, denied delivery modes, disallowed OpenBao policies, audit/revocation expectations, and secret-looking marker rejection. ## T03 - Configure bounded OpenBao token roles and policies ```task id: RAILIANCE-WP-0005-T03 status: wait priority: high state_hub_task_id: "d8498e3b-b2fb-47b7-ab88-cd6592c1807e" ``` Create idempotent scripts/manifests for OpenBao token roles or equivalent lease issuance paths that can generate child tokens only for approved policies and TTLs. Start with warden-sign. Acceptance: - A non-root issuer path can create a warden-sign token with bounded TTL. - The resulting token cannot administer OpenBao and can only call the SSH sign paths allowed by openbao/policies/warden-sign.hcl. - Verification proves the token can run ops-warden vault signing and cannot list unrelated secrets. **2026-06-26:** Added the source-side OpenBao token-grant implementation for the `ops-warden/warden-sign` pilot: issuer policy `openbao/policies/credential-broker-warden-sign-issuer.hcl`, idempotent apply and verify scripts, Make targets for dry-run/live apply/live verification, and catalog validation for `openbao.issuer_policy`. Dry-run validation is expected to work offline. Live closure still requires an approved OpenBao operator token path and successful runs of `make openbao-configure-token-grants` and `make openbao-verify-token-grants-smoke`, so T03 remains `progress`. **2026-06-27:** Attempted the live idempotent apply with `make openbao-configure-token-grants OPENBAO_TOKEN_GRANT_ARGS=--use-token-helper`. OpenBao was reachable and unsealed, but the pod token helper received `403 permission denied` while writing `sys/policies/acl/credential-broker-warden-sign-issuer`. T03 is now `wait` until an approved OpenBao issuer/platform-admin path applies the policy and role, or the pod token helper is granted that narrow capability. ## T04 - Build credential helper MVP ```task id: RAILIANCE-WP-0005-T04 status: wait priority: high state_hub_task_id: "0c543cb3-36cb-4b25-9a58-de8efc1216c9" ``` Build a small CLI/helper in this repo first, for example credential or openbao-lease, with request, exec, status, and revoke commands. Acceptance: - credential exec can run the ops-warden production smoke with VAULT_TOKEN only in the child process environment. - request returns a wrapped token or lease handle by default, not the raw token. - status and revoke work by non-secret lease handle/accessor. - The helper redacts token-looking values from logs and refuses to run in verbose modes that would print secrets. **2026-06-26:** Added `scripts/credential.py` as the source helper MVP with `request`, `exec`, `status`, and `revoke` subcommands. The helper validates the grant catalog, enforces purpose and TTL bounds, defaults `request` to a local mode-0600 token file plus non-secret accessor metadata, supports response-wrap handoff, injects `VAULT_TOKEN` only into the child process for `exec`, redacts token-looking child output, rejects caller-supplied token env assignments, and revokes exec tokens by accessor in a `finally` block. Added Make dry-run and ops-warden smoke targets. T04 remains `progress` until a live OpenBao issuer token is available to prove `credential-exec-ops-warden-smoke` end to end. **2026-06-27:** Extended the helper with optional flex-auth preflight, non-secret State Hub lifecycle metadata, actor/subject binding fields, `--decision-id` support, and Kubernetes-auth delegation output. Fixed the Make surface so global helper flags such as `--use-token-helper` are passed before the subcommand. T04 is now `wait` on the same OpenBao live gate as T03 before ops-warden smoke can be proven end to end. ## T05 - Implement secure delivery modes ```task id: RAILIANCE-WP-0005-T05 status: wait priority: high state_hub_task_id: "66f3cd6d-7520-4584-90b8-672866ef3490" ``` Support safe delivery modes for different runtime contexts. Required modes: - exec-env: inject credential into one child process, then forget it; - response-wrap: produce a single-use OpenBao wrapping token for attended handoff; - local-token-file: write mode 0600 under an ignored local state directory, with TTL metadata and cleanup; - kubernetes-auth: use service-account-bound auth for in-cluster workloads instead of handing them tokens manually. Acceptance: - No delivery mode requires pasting the secret into chat or State Hub. - local-token-file paths are gitignored and rejected by secret scans if accidentally staged. - response-wrap unwraps once and fails on second use. **2026-06-27:** Source support now covers all four delivery modes: `exec-env`, `response-wrap`, `local-token-file`, and `kubernetes-auth`. The helper refuses caller-supplied token env assignments, writes local leases under the ignored `.local/credential-leases/` path with mode `0600`, and emits only service account auth metadata for Kubernetes-auth. T05 is `wait` until live response-wrap single-use behavior and the OpenBao-backed exec path are verified with an approved issuer token. ## T06 - Integrate KeyCape identity and agent subject binding ```task id: RAILIANCE-WP-0005-T06 status: done priority: medium state_hub_task_id: "e1dd5973-bf2b-4aa9-842e-9f530afa1ab6" ``` Define how humans and agents authenticate to request grants. Acceptance: - Human operator path uses KeyCape/OIDC with MFA where required. - Agent/service path has a documented subject id shape compatible with IAM profile claims and existing actor naming. - Headless automation uses Kubernetes auth or an explicitly approved non-interactive identity; it does not reuse a human token. **2026-06-27:** Documented the identity contract in `docs/credential-broker.md`: KeyCape/OIDC with MFA for human operators, stable IAM-compatible subjects for agents and CI, and Kubernetes service-account subjects for headless workloads. The helper now exposes `--actor`, `--actor-type`, and `--subject`, and validates actor type against the grant catalog. T06 is done source-side. ## T07 - Add flex-auth preflight authorization and State Hub request metadata ```task id: RAILIANCE-WP-0005-T07 status: wait priority: medium state_hub_task_id: "1269bb58-0699-43ef-aa4f-43bc49c61a49" ``` Before issuing a lease, optionally call flex-auth with actor, subject, grant, purpose, TTL, audience, and requested delivery mode. Record non-secret request metadata and decision ids in State Hub when available. Acceptance: - flex-auth can deny overbroad TTL, wrong actor type, wrong purpose, or disallowed delivery mode. - State Hub records request lifecycle without token values. - The helper works in offline/degraded mode only for pre-authorized local flows; it never caches new secret material in State Hub. **2026-06-27:** Added optional flex-auth preflight via `--flex-auth-url` / `FLEX_AUTH_URL`, strict `--require-flex-auth`, provided decision ids via `--decision-id`, and opt-in State Hub lifecycle notes via `--record-state-hub`. The helper records only non-secret metadata. T07 is `wait` until a live flex-auth credential authorization endpoint is available and the OpenBao live gate is cleared. ## T08 - Integrate ops-warden smoke and routing catalog ```task id: RAILIANCE-WP-0005-T08 status: wait priority: high state_hub_task_id: "4571d4c9-d4de-4ee9-97e0-ff03e49e65ec" ``` Replace the manual VAULT_TOKEN step in ops-warden smoke docs with the credential helper flow and update the credential routing catalog. Acceptance: - FLEX-WP-0007 T4 can be run with one command once the grant is configured: credential exec --grant ops-warden/warden-sign --ttl 15m -- SMOKE_VAULT=1 /home/worsch/ops-warden/scripts/policy_gate_production_smoke.sh - ops-warden docs still make clear it owns SSH cert signing, not OpenBao token vending. - warden route find VAULT_TOKEN points to this railiance-platform flow. **2026-06-27:** Added `make credential-exec-ops-warden-smoke` for the intended one-command smoke and confirmed credential routing locally with `uv run warden route show openbao-api-key --json`: OpenBao/API/dynamic lease needs belong to `railiance-platform`; ops-warden executes SSH cert issuance only. T08 is `wait` because this workspace cannot update the external ops-warden routing catalog and the live OpenBao grant apply is still denied. ## T09 - Verification, audit, and red-team checks ```task id: RAILIANCE-WP-0005-T09 status: wait priority: high state_hub_task_id: "78d1db83-12fb-4ac2-95eb-54c91ac125b5" ``` Add tests and operator verification for the complete flow. Acceptance: - Unit tests cover grant validation, TTL bounds, redaction, and delivery-mode restrictions. - Dry-run tests require no secrets. - Live smoke proves OpenBao audit logs record issuance and use. - Negative tests prove denied grants do not mint tokens. - Documentation includes emergency revocation and cleanup commands. **2026-06-27:** Added `tests/test_credential_helper.py` and `make credential-tests` covering TTL bounds, actor-type restrictions, token redaction, unsafe env rejection, local lease mode/cleanup, Kubernetes-auth delegation, and gitignore coverage for local lease files. Offline validation is passing. T09 is `wait` until live OpenBao audit evidence, response-wrap unwrap-once evidence, and negative live mint checks can be collected. ## T10 - Rollout and migration ```task id: RAILIANCE-WP-0005-T10 status: wait priority: medium state_hub_task_id: "44ce4082-fa8f-44d0-8f86-172d14ecfb0e" ``` Roll out in phases. Phases: 1. warden-sign VAULT_TOKEN pilot for flex-auth/ops-warden smoke. 2. Platform-readonly token helper for diagnostics. 3. Workload-specific grants for app repositories. 4. Optional split to a dedicated credential-broker repo if code grows beyond railiance-platform ownership. Acceptance: - The VAULT_TOKEN blocker from FLEX-WP-0007 is cleared without manual token paste. - Operators have a documented fast path and a break-glass path. - State Hub, ops-warden, key-cape, and flex-auth docs link to the same routing truth. **2026-06-27:** Documented rollout phases, emergency revocation, delivery modes, identity binding, flex-auth preflight, State Hub metadata, and routing ownership in `docs/credential-broker.md`. T10 is `wait` on the live warden-sign pilot and external routing-doc/catalog updates. ## Exit Criteria - A policy-approved actor can request or exec with a short-lived OpenBao token without seeing or pasting the raw token. - The ops-warden vault-backed smoke can run without manual VAULT_TOKEN handling. - All issued credentials are bounded, auditable, and revocable. - State Hub and workplans contain only non-secret metadata. - The credential routing catalog points token/dynamic-lease requests to railiance-platform.