# KeyCape `secrets-engine-openbao` client secret: consumer custody Rewritten 2026-10-06 by secrets-engine. Tracked in SECRETS-WP-0012. **Source and documentation only.** No value was generated, fetched or written; no host, cluster, OpenBao or KeyCape state was changed. ## Direction Founder direction 2026-10-06: the `secrets-engine-openbao` KeyCape client secret lives **only in OpenBao**. There is no persistent workstation copy. ops-mason decided placement and the read lane in `ops-mason/plans/keycape-secrets-engine-openbao-client-custody.md` (commit `658cdbc`, workplan MASON-WP-0009; status *reviewed*, founder approvals A1–A3 pending). That direction and plan **supersede the earlier consumer-file packet and its decisions D1–D4** (host, OS user, persistent file path, rotation cadence). The consumer-file custody child in CCR-2026-0033 is out of direction and is being revised by railiance-platform. ## Coordinates (non-secret) | Item | Value | |---|---| | Authoritative entry | KV v2 mount `platform`, path `workloads/keycape/secrets-engine-openbao` (API `platform/data/workloads/keycape/secrets-engine-openbao`), field `CLIENT_SECRET` | | Issuer copy | `sso` Secret `keycape-secrets-engine-openbao-client` key `client-secret`, via ESO on Kubernetes auth (platform/KeyCape; unchanged) | | Consumer copy | **none** — the value exists only in process memory for one command | | Consumer read lane | OIDC mount `auth/netkingdom`, role `keycape-secrets-engine-openbao-operator-workload-kv-read`, policy `workload-kv-read-keycape-secrets-engine-openbao-operator` (read on the data path, `update` on `auth/token/revoke-self`, nothing else), 5m TTL, 4 uses, bound `groups=net-kingdom-admins` | | Companion config | `BAO_ADDR=http://127.0.0.1:18200` (operator tunnel; bao.coulomb.social retired 2026-09-15), `SECRETS_ENGINE_KEYCAPE_TOKEN_URL=https://kc.coulomb.social/token`, `SECRETS_ENGINE_KEYCAPE_ISSUER=https://kc.coulomb.social`, `SECRETS_ENGINE_OPENBAO_JWT_LOGIN=` | The mount, path, field and role are pinned in `service_auth.OpenBaoOidcClientSecretSource`; any other value is refused. ## What the code does (per command that selects `service-jwt`) 1. Runs `bao login -method=oidc -path=netkingdom -no-store -format=json role=keycape-secrets-engine-openbao-operator-workload-kv-read`. The browser/MFA prompt goes to the operator's terminal (stderr); the login response is captured from a **stdout pipe into memory**. `BAO_TOKEN` and `VAULT_TOKEN` are removed from the child environment, and `-no-store` keeps the token out of the token helper. For non-token methods the CLI does not send a helper-stored token. The token never appears in argv, env, a file, a log or evidence. 2. One in-process HTTPS `GET /v1/platform/data/workloads/keycape/secrets-engine-openbao` with the token only in the `X-Vault-Token` header; extracts `CLIENT_SECRET` (must be exactly one non-empty token). 3. `POST /v1/auth/token/revoke-self` with the same token — always, also when the read fails. 4. The value goes in memory to the KeyCape `/token` `client_credentials` exchange (HTTP Basic only), then the existing OpenBao JWT login on the platform role and its self-revoke at command end. The value is dropped when the exchange request is built. Refusals: `SECRETS_ENGINE_KEYCAPE_CLIENT_SECRET_FILE` set → refused for this profile; a `KeyCapeServiceAuthConfig` with a file → refused; a provider for this profile without the in-memory source → refused. `service-jwt` still never falls back to `--bootstrap-token-file`, `BAO_TOKEN` or AppRole. The approval client (`secrets-engine-approval`) keeps its own file source and `service-auth check-secret-file` for it. Evidence is value-free: `secret_source=openbao-oidc-in-memory`, the operator and engine accessor fingerprints (12 hex chars of SHA-256), and both revoke-self results. Tests with synthetic values: `tests/test_openbao_secret_source.py`. ## First live use (founder-run, attended) Run only after **all** of: founder approval of the ops-mason plan (A1), the revised CCR-2026-0033 applied by railiance-platform (A2/A3: KV entry written, operator OIDC role and policy, ESO reader, JWT role), the KeyCape `secrets-engine-openbao` registration live and KeyCape restarted after the KV write (KEY-WP-0037), and the platform JWT login contract file published. This is the evidence step for KEY-WP-0037-T04 / RPF-WP-0035-T02 and plan verification item 6. ```bash cd /home/worsch/secrets-engine && \ env -u BAO_TOKEN -u VAULT_TOKEN -u SECRETS_ENGINE_KEYCAPE_CLIENT_SECRET_FILE \ BAO_ADDR=http://127.0.0.1:18200 \ SECRETS_ENGINE_KEYCAPE_TOKEN_URL=https://kc.coulomb.social/token \ SECRETS_ENGINE_KEYCAPE_ISSUER=https://kc.coulomb.social \ SECRETS_ENGINE_OPENBAO_JWT_LOGIN=/home/worsch/.secrets-engine/keycape/openbao-jwt-login.yaml \ uv run secrets-engine service-auth probe-openbao --json ``` A browser opens for the KeyCape login with MFA (an existing session is reused); stderr shows the `bao` OIDC prompt. Expected stdout, exit 0 — one line, only these keys, `` being 12 hex characters: ```json {"auth_provider": "service-jwt", "established": true, "ok": true, "operator_revocation_attempted": true, "operator_revocation_succeeded": true, "operator_session_handle": "", "revocation_attempted": true, "revocation_succeeded": true, "secret_source": "openbao-oidc-in-memory", "session_handle": ""} ``` Anything else is a failure to record, not to retry blindly: exit 1 with `error: ...` on stderr names the failed step (OIDC login exit code, KV HTTP status, KeyCape HTTP status, JWT login) and, for a source failure, both operator revoke flags. `ok: false` means a revoke-self did not succeed; the token stays TTL/use-bound (5m/4 uses, engine token per platform role). Record the JSON line only; never a value, token or full accessor. ## Rotation, revocation, rollback Platform-owned (plan §3.5): roll-forward KV writes with `cas=`, event-driven plus 90 days, then KeyCape restart. The consumer needs no action: the next command reads the latest version. Exchanges fail closed (401) between the KV write and the KeyCape restart. On compromise KeyCape/NetKingdom disable the registration first. secrets-engine can neither write nor rotate this entry and never reads an old version. ## Not established No live OIDC login, KV read, revoke-self, KeyCape exchange or JWT login has run. The operator role, policy, KV entry and JWT contract file do not exist yet as far as this repository knows. `bao login -no-store -format=json` output shape for OIDC is taken from the CLI contract and is first exercised by the founder run above.