2026-06-29 12:55:58 +02:00
---
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
2026-06-30 01:01:55 +02:00
status: finished
2026-06-29 12:55:58 +02:00
owner: codex
topic_slug: custodian
created: "2026-06-29"
2026-06-30 01:01:55 +02:00
updated: "2026-06-30"
2026-06-29 12:56:46 +02:00
state_hub_workstream_id: "eb1bdff7-909c-4391-8b95-6baf1feb3a54"
2026-06-29 12:55:58 +02:00
---
# 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),
2026-06-30 01:01:55 +02:00
after which the verified gate can be banked until `policy.enabled: true`
is appropriate for the ecosystem maturity stage.
2026-06-29 12:55:58 +02:00
## 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/<role>` 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
2026-06-29 16:58:16 +02:00
status: done
2026-06-29 12:55:58 +02:00
priority: high
2026-06-29 12:56:46 +02:00
state_hub_task_id: "278e9a20-eaa6-47a8-9dd7-62952961d520"
2026-06-29 12:55:58 +02:00
```
2026-06-29 16:58:16 +02:00
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.
2026-06-29 12:55:58 +02:00
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
2026-06-29 16:58:16 +02:00
status: done
2026-06-29 12:55:58 +02:00
priority: high
2026-06-29 12:56:46 +02:00
state_hub_task_id: "f4b209e5-c900-4f5e-bb0c-f0c5b27b70c2"
2026-06-29 12:55:58 +02:00
```
2026-06-29 16:58:16 +02:00
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.
2026-06-29 12:55:58 +02:00
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
2026-06-30 01:01:55 +02:00
status: done
2026-06-29 12:55:58 +02:00
priority: high
2026-06-29 12:56:46 +02:00
state_hub_task_id: "4b414788-d670-496b-9c57-074464a012c4"
2026-06-29 12:55:58 +02:00
```
2026-06-29 16:58:16 +02:00
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.
2026-06-30 00:52:05 +02:00
2026-06-29: Attended ops-warden follow-up message
`92de2614-061f-4241-aa13-a3ce72ad41d5` . Production OpenBao is unsealed and the
dry-run apply plan is valid for exact `update` grants on `ssh/sign/agt-role` ,
`ssh/sign/adm-role` , and `ssh/sign/atm-role` . Live apply remains blocked because
`secrets-engine route warden-sign --json` reports `decision_status: missing`
for `SECRETS-WP-0004` , and the documented lane bootstrap token file
`~/.secrets-engine/bootstrap/prod-warden-sign.token` is absent. A broader
platform-admin token file exists with mode `600` ; it was not used to bypass the
missing lane decision or the documented bootstrap handoff.
2026-06-30: Approval mirror `.decisions/SECRETS-WP-0004.yaml` is now present
and `secrets-engine route warden-sign --json` reports `decision_status:
resolved` for canonical decision ` 4589dcb7-c0df-4073-9a0b-4f80a0fcdb93`.
2026-06-30 01:01:55 +02:00
Readiness remained false at that checkpoint because the OpenBao `warden-sign`
policy/AppRole had not yet been applied (`metadata_applied: false` ), and the
documented lane bootstrap token file was still absent. Live apply and handoff
were not executed in that checkpoint.
2026-06-30 closeout: ops-warden later reported that production OpenBao was
unsealed, the secrets-engine `warden-sign` lane was applied, and a scoped
AppRole token with `ssh/sign/agt-role` update capability was verified through
the vault-backed smoke. No token value, role_id, secret_id, token accessor, or
raw smoke log is recorded here. The documented bootstrap-token file path remains
part of the hardening/audit backlog, but live lane metadata is no longer the
blocker.
2026-06-30 00:52:05 +02:00
2026-06-29 12:55:58 +02:00
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
2026-06-30 01:01:55 +02:00
status: done
2026-06-29 12:55:58 +02:00
priority: high
2026-06-29 12:56:46 +02:00
state_hub_task_id: "52b5cf88-029f-4f4b-8fe9-0a8dc30378b5"
2026-06-29 12:55:58 +02:00
```
2026-06-29 16:58:16 +02:00
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 19:07:36 +02:00
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.
2026-06-30 00:52:05 +02:00
2026-06-29: ops-warden follow-up message
`92de2614-061f-4241-aa13-a3ce72ad41d5` requested the AppRole handoff as the
preferred end-state. Handoff was not executed because live apply is still gated
by the missing approved decision and missing lane bootstrap token. Once those
blockers clear, the intended handoff remains single-use AppRole material written
to mode-0600 files outside any Git worktree and delivered to Bernd out-of-band.
2026-06-30: The decision gate is now resolved by the offline approval mirror,
so handoff is waiting on live OpenBao apply plus an attended, out-of-band
bootstrap path. No `role_id` , `secret_id` , token value, token accessor, or
smoke output was written to Git or State Hub.
2026-06-30 01:01:55 +02:00
2026-06-30 closeout: the scoped warden-sign handoff path was exercised by the
operator/ops-warden outside Git and State Hub. The only recorded evidence is the
non-secret result: the vault-backed smoke used backend `vault` and policy
decision `decision:032b096c433ad80c` . `policy.enabled` is intentionally left off
until testing/production maturity; that is a separate operator posture decision,
not an unfinished secrets-engine handoff.
2026-06-29 12:55:58 +02:00
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=<scoped> ~/ops-warden/scripts/policy_gate_production_smoke.sh` .
## T05 - Confirm the joint smoke and signal ops-warden
```task
id: SECRETS-WP-0004-T05
2026-06-30 01:01:55 +02:00
status: done
2026-06-29 12:55:58 +02:00
priority: medium
2026-06-29 12:56:46 +02:00
state_hub_task_id: "aa0f281e-976d-4fcd-b8a3-78582117f86f"
2026-06-29 12:55:58 +02:00
```
2026-06-29 16:58:16 +02:00
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
2026-06-30 01:01:55 +02:00
remained waiting on live OpenBao apply, handoff, and operator-run smoke evidence
at that checkpoint.
2026-06-30 closeout: ops-warden reported the joint smoke passed with non-secret
evidence only: allow path `warden sign agt-state-hub-bridge` returned
policy_decision_id `decision:032b096c433ad80c` , excessive TTL `--ttl 999` was
rejected with `ttl_out_of_bounds` before OpenBao, and the vault-backed allow path
used the scoped warden-sign lane. flex-auth closed `FLEX-WP-0007-T04` from this
evidence, so secrets-engine can close the credential/capability lane too.
2026-06-29 16:58:16 +02:00
2026-06-29 12:55:58 +02:00
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/<allowlisted-role>` 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.
2026-06-30 01:01:55 +02:00
## Closeout Evidence
2026-06-30: SECRETS-WP-0004 is finished from the same non-secret smoke evidence
used to close `FLEX-WP-0007-T04` :
- `warden-sign` policy/AppRole lane applied in production OpenBao after operator unseal.
- Scoped token capability verified for `ssh/sign/agt-role` update during the vault-backed smoke.
- Allow smoke: `warden sign agt-state-hub-bridge` -> `decision:032b096c433ad80c` .
- Deny smoke: `--ttl 999` -> `ttl_out_of_bounds` before OpenBao signing.
- No raw token, AppRole `secret_id` , `role_id` , token accessor, or smoke log was written to Git, State Hub, prompts, chat, or normal logs.
- `policy.enabled` remains off by build-stage maturity decision and can be flipped later by the ops-warden operator when testing/production posture requires live enforcement.