--- id: SECRETS-WP-0004 type: workplan title: "Provision a scoped warden-sign token lane (ops-warden / FLEX-WP-0007 T4)" domain: infotech repo: secrets-engine status: active owner: codex topic_slug: custodian created: "2026-06-29" updated: "2026-06-29" state_hub_workstream_id: "eb1bdff7-909c-4391-8b95-6baf1feb3a54" --- # SECRETS-WP-0004 - Provision a scoped warden-sign token lane ## Goal Give ops-warden a scoped OpenBao credential that can run `warden sign` over the HTTP API, minted and custodied by secrets-engine. Model it as a first-class secrets-engine **auth-capability lane** (an OpenBao policy + AppRole granting a narrow capability), not a KV value. Post the non-secret pointers to State Hub and hand the token/secret_id to the operator out-of-band. This unblocks FLEX-WP-0007 T4 (the joint OpenBao + policy-gate production smoke), after which `policy.enabled: true` can go live on CoulombCore. ## Context ops-warden raised this on State Hub (msg 077ac90d, 2026-06-28). warden's vault backend signs over the HTTP API (`X-Vault-Token`) and holds no standing OpenBao token (conduit-not-broker), so token custody/minting is squarely secrets-engine's lane. The operator cannot mint one interactively: the `bao` CLI is not installed on CoulombCore and `bao login -method=oidc` is therefore unavailable — hence the preference for an AppRole the operator can use non-interactively. Target backend is the **production** OpenBao at `https://bao.coulomb.social`, mount `ssh`. Actors in scope: `agt-state-hub-bridge` (primary), `agt-codex-interhub-bootstrap`, `adm-example`, `atm-backup-daily`. The signing roles are `agt-role`, `adm-role`, `atm-role`. This is the first non-KV, non-npm lane and the first lane that touches production OpenBao, so it also exercises parts of the engine the npm pilot did not. ## Design Constraints - No token value or AppRole `secret_id` in Git, State Hub, chat, prompts, workplans, or normal logs — pointers only on the hub, value out-of-band. - The `warden-sign` policy must be narrow: `update` on `ssh/sign/` for the allowlisted roles only. No broader `ssh/*`, no `sys/`, `auth/token/`, `identity/`, no root/sudo. - Production apply uses a mode-0600 bootstrap token outside any repo, revocable, recorded in `docs/hardening-backlog.md` H0. - ops-warden takes no custody; secrets-engine owns minting and revocation. ## Tasks ## T01 - Extend the catalog for auth-capability (non-KV) lanes ```task id: SECRETS-WP-0004-T01 status: done priority: high state_hub_task_id: "278e9a20-eaa6-47a8-9dd7-62952961d520" ``` 2026-06-29: Implemented `kind: auth-capability` catalog support, fieldless non-KV validation, KV provisioning bypass/rejection, route semantics, and dry-run plan/apply handling while preserving the existing KV/npm lane behavior. Add a lane kind that represents an OpenBao auth credential (AppRole + policy) granting a capability on a mount, rather than a KV value. The current schema assumes a KV `mount/path/fields` value and `provision --from-file`; an auth-capability lane has no stored value — the deliverable is the minted token / `secret_id`. Acceptance: - The schema validates a `warden-sign` auth-capability lane (kind explicit). - KV-only assumptions (`kv_data_path`, from-file provisioning, value verification) are bypassed for this kind without weakening the npm KV path. - `plan`/`apply` produce a policy + AppRole for the lane. ## T02 - Define the warden-sign policy and AppRole ```task id: SECRETS-WP-0004-T02 status: done priority: high state_hub_task_id: "f4b209e5-c900-4f5e-bb0c-f0c5b27b70c2" ``` 2026-06-29: Added `catalog/warden-sign.yaml`, exact `warden-sign` policy/AppRole planning, guard coverage rejecting broad `ssh/*`, `sys/`, `auth/token/`, `identity/`, wildcard, root-like, and non-`update` grants, plus tests for the allowlist and denial probes. Production stage policy now has exact `warden-sign` policy/AppRole management exceptions only. Author the `warden-sign` ACL policy and AppRole. Policy: `update` on `ssh/sign/agt-role` (plus `adm-role`, `atm-role`) on the `ssh` mount; nothing else. AppRole `warden-sign` bound to that policy with a short token TTL, usable non-interactively via `role_id` + `secret_id` (no `bao` CLI needed). Acceptance: - The policy lists exact allowed/denied paths; the guards in `roles.py` refuse any broader variant (`ssh/*`, `sys/`, `auth/token/`, `identity/`, wildcards, root). - Negative check: a token from this AppRole can `sign` only the allowlisted roles and is denied elsewhere on the `ssh` mount. - The AppRole issues a scoped token carrying only the sign capability. ## T03 - Apply against production OpenBao via bootstrap token ```task id: SECRETS-WP-0004-T03 status: wait priority: high state_hub_task_id: "4b414788-d670-496b-9c57-074464a012c4" ``` 2026-06-29: Source apply path is implemented and dry-run verified. Live apply is waiting on an approved SECRETS-WP-0004 decision/workplan gate plus a mode-0600 production bootstrap token outside any repo. The pending token path/revocation row is recorded in `docs/hardening-backlog.md` H0. Apply the policy + AppRole on `https://bao.coulomb.social` using a mode-0600 bootstrap token stored outside any repo. Idempotent re-apply. Acceptance: - The `warden-sign` policy and AppRole exist on the production OpenBao. - The bootstrap token file is recorded in `docs/hardening-backlog.md` H0 with an explicit revocation task and TTL. - Apply evidence is non-secret (policy/role names, mount, addr, status — no value). ## T04 - Out-of-band handoff and non-secret pointers ```task id: SECRETS-WP-0004-T04 status: wait priority: high state_hub_task_id: "52b5cf88-029f-4f4b-8fe9-0a8dc30378b5" ``` 2026-06-29: Added `secrets-engine handoff` and `docs/warden-sign-auth-capability.md`. Handoff writes `role_id` and a fresh single-use `secret_id` to caller-chosen mode-0600 files outside Git worktrees and records only non-secret file paths/TTL metadata. Execution is waiting on live apply and attended out-of-band operator delivery. 2026-06-29: ops-warden follow-up message `f5b276a3-b90f-454f-b883-83eee7c69616` asked for the OIDC login pointer instead of AppRole handoff because Bernd can mint the scoped token on his workstation. Added the non-secret pointer: `bao login -method=oidc -path=netkingdom role=warden-sign`, role path `auth/netkingdom/role/warden-sign`, bound to `groups=["net-kingdom-admins"]` and attaching only policy `warden-sign`. Replied to ops-warden in State Hub message `0bce1e08-58c2-4db4-b06c-f86cf8142273`; no token value, role_id, secret_id, or token accessor was included. Define and execute the handoff: mint a fresh `secret_id`, deliver it (with the `role_id`) to the operator out-of-band; warden does `approle login` to obtain a `VAULT_TOKEN`. Post the non-secret pointers on the ops-warden thread (policy name, AppRole name, mount `ssh`, addr `https://bao.coulomb.social`, token TTL, status). Acceptance: - Pointers are posted to State Hub; no token value or `secret_id` appears there or in Git/logs. - The operator can run, on CoulombCore: `FLEX_AUTH_EXTERNAL=1 SMOKE_VAULT=1 VAULT_TOKEN= ~/ops-warden/scripts/policy_gate_production_smoke.sh`. ## T05 - Confirm the joint smoke and signal ops-warden ```task id: SECRETS-WP-0004-T05 status: wait priority: medium state_hub_task_id: "aa0f281e-976d-4fcd-b8a3-78582117f86f" ``` 2026-06-29: Offline tests pass (`59 passed, 2 skipped`) and route/dry-run CLI checks produce non-secret pointers. Joint production smoke and ops-warden signal remain waiting on live OpenBao apply, handoff, and operator-run smoke evidence. Confirm the unblock end to end, then reply to ops-warden (msg 077ac90d) with the pointers and runbook alignment. The reply is the explicit "we will signal ops-warden when done" step. Acceptance: - Allow path resolves to `backend: vault` + a `policy_decision_id`; deny path (ttl 999) is rejected by flex-auth before OpenBao. - ops-warden is replied to on State Hub with the non-secret pointers. - A revocation/rotation task exists for the minted credential. ## Exit Criteria - A narrow `warden-sign` policy + AppRole exist on production OpenBao, minted and custodied by secrets-engine, granting only `ssh/sign/` update. - The operator can obtain a scoped `VAULT_TOKEN` non-interactively without `bao`. - FLEX-WP-0007 T4 joint smoke is unblocked (or blocked only on an explicit external condition). - ops-warden has the non-secret pointers; no secret value crossed State Hub. - The bootstrap token and the minted credential have revocation tasks.