ops-warden/wiki/playbooks/catalog-lane-promotion.md
tegwick c93e3c9b43
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Ship WARDEN-WP-0030: delegation register for every catalog lane
Every execution position is now explicit. Catalog entries carry
delegation.mode (permanent / native / interim) with intended owner and
blocker. warden route gaps lists the interim set. Promotion requires
the ownership question. Doctrine lives in AccessRouting.md; the
register was published to the named owner repos.
2026-08-15 20:54:58 +02:00

129 lines
No EOL
6 KiB
Markdown

# Catalog Lane Promotion — draft → active
Date: 2026-07-01
Workplan: WARDEN-WP-0023 T05
`registry/routing/catalog.yaml` entries start as **`draft`** until an owner-confirmed
concrete path exists. Draft lanes are hidden from default `warden route find` unless
`--all` is passed.
---
## Promotion checklist
Before changing `status: draft``status: active`:
| # | Criterion | Evidence |
| --- | --- | --- |
| 1 | **Owner confirmed** | Owner repo workplan or State Hub note naming the lane ready |
| 2 | **Concrete path** | Real OpenBao path, grant id, or exec command — no unresolved `<placeholders>` in the primary handoff |
| 3 | **Playbook** | `wiki/playbooks/<id>.md` with `#worker-checklist` section |
| 4 | **Exec routing** | `exec_owner` + native command **or** `exec_capable: true` with tested `warden access` proxy |
| 5 | **Resolvable** | `warden route show <id> --json` shows `resolvable: true` when placeholders are documented |
| 6 | **Tests** | Routing test or smoke proving lookup + handoff shape (no secret values in fixtures) |
| 7 | **Review date** | Update `reviewed:` in catalog entry |
| 8 | **Verification** | Positive + negative proof via **`bao token capabilities`** — never `bao kv get` (see below) |
| 9 | **Rotation guidance** | Secret-vending lanes carry a `rotation:` block; `warden rotate-guide <id>` returns steps. Enforced by the `catalog_rotation_coverage` scorecard check (WP-0026 T06) |
| 10 | **Delegation** | Entry carries a `delegation:` block (WP-0030). `mode: permanent` only for ops-warden's own front door. `mode: native` when the owner already fronts it. `mode: interim` requires `intended_owner` and `blocked_on`; the promotion note must name both and the retirement condition |
Promotion PR touches: `registry/routing/catalog.yaml`, playbook, optional
`tests/test_routing.py`, and a one-line note in `wiki/CredentialRouting.md` draft table.
If `delegation.mode` is `interim`, the promotion note (PR body or State Hub
progress) must state:
1. who the intended owner is
2. what is missing (`blocked_on`)
3. what would let ops-warden step back (`exec_owner` + proven owner front door)
A proxy that works is not enough to promote without answering the ownership
question. `warden route gaps` must list the lane after merge if it is interim.
---
## Capabilities-safe lane verification (WARDEN-WP-0026 T01)
**Verifying a lane must never read the secret *data*.** A negative deny-test that
runs `bao kv get <path>` will, if the deny fails (e.g. a privileged token
fallback), print the secret value into a logged context — this is exactly the
2026-07-16 CCR-2026-0004 disclosure. Prove *allow/deny* with
`bao token capabilities`, which returns the capability list, not the value.
For KV v2, capabilities are checked against the **API data path**
(`<mount>/data/<lane-path>`), not the `kv get` logical path.
```bash
# Positive: the lane's own OIDC identity can read the data path.
bao login -method=oidc -path=netkingdom role=<lane-role> # caller identity
bao token capabilities "$(bao print token)" platform/data/<lane-path>
# → expect the list to include: read
# Negative: a default-only identity is denied — no value is ever read.
DEFAULT_TOKEN=$(bao token create -policy=default -field=token) # if denied, STOP — do not fall back
bao token capabilities "$DEFAULT_TOKEN" platform/data/<lane-path>
# → expect: deny
```
- **Never** substitute `bao kv get` for the checks above. Reading a value to
"confirm it's there" is the anti-pattern; presence is proven by `read` in the
capability list.
- If `bao token create -policy=default` is itself denied for your identity, that
is a *pass for the deny direction***do not** fall back to your privileged
login token to force the read.
- Fetching a value **for use** (`--field` into an env var or file, or
`warden access … --field`) is a separate, intended action — not verification.
Record the capability lists (allow/deny) as the promotion evidence; they contain
no secret material and are safe for CCRs, State Hub, and Git.
---
## Worked examples (already active)
**`ops-warden-warden-sign-token`** — promoted 2026-07-01 after RAILIANCE-WP-0005:
- Owner: `railiance-platform` credential broker
- Concrete grant: `ops-warden/warden-sign`
- Playbook: `wiki/playbooks/ops-warden-warden-sign-token.md`
- Smoke: `make credential-exec-ops-warden-smoke`
**`issue-core-ingestion-api-key`** — promoted 2026-07-02 after RAILIANCE-WP-0009
(CCR-2026-0002): KV path live, ExternalSecret `issue-core/issue-core-runtime`
SecretSynced, positive + negative verification audit-logged.
**`openrouter-llm-connect`** — promoted 2026-07-02 after RAILIANCE-WP-0010
(CCR-2026-0003): KV path live, ExternalSecret
`activity-core/llm-connect-provider-secrets` SecretSynced, llm-connect rolled
out on the OpenBao-delivered value, positive + negative verification audit-logged.
---
## Draft lanes (2026-07-17)
| Catalog `id` | Blocker |
| --- | --- |
| `object-storage-sts` | NK-WP-0007 vending path not production-exercised |
| `database-dynamic-credentials` | OpenBao database engine role paths TBD per workload |
**Promoted 2026-07-16:** `railiance-backup-offsite-lane` — CCR-2026-0004
capabilities-safe re-verify (WP-0026 T07); primary field `NC_WEBDAV_TOKEN`;
`risk: high` + EXPOSED taint on version 2 (operator may rotate optionally).
**Promoted 2026-07-17:** `binky-company-email-imap` — CCR-2026-0007 on mount
`tenants/`; founder provisioned (KV version ≥2); capabilities-safe verify;
primary field `IMAP_PASSWORD`; `risk: high`. Host `imap.ionos.de:993`.
**Tenant path (WP-0028):** new client secrets use mount `tenants/`, not
`platform/workloads/`. See `wiki/playbooks/tenant-secret-onboarding.md`.
Re-run promotion when the owning repo closes the blocker; do not promote on
playbook prose alone.
---
## See also
- `wiki/CredentialRouting.md` — draft table index
- `wiki/playbooks/ops-warden-warden-sign-token.md` — promotion reference
- `wiki/AccessRouting.md#interim-custodianship` — delegation register doctrine
- `warden route gaps` — queryable interim register