diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index d8cc04c..40db1ba 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -128,3 +128,4 @@ | task | WARDEN-WP-0033-T03 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md | | task | WARDEN-WP-0033-T04 | wait | — | workplans/WARDEN-WP-0033-native-lane-handoff.md | | task | WARDEN-WP-0033-T05 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md | +| task | WARDEN-WP-0033-T06 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md | diff --git a/registry/generated/high-risk-data-paths.yaml b/registry/generated/high-risk-data-paths.yaml index 2cb9ecd..0c3bf94 100644 --- a/registry/generated/high-risk-data-paths.yaml +++ b/registry/generated/high-risk-data-paths.yaml @@ -10,12 +10,12 @@ # declares it, and is null where the field set has not been established -- # null means unknown, never 'one field'. -generated_at: "2026-08-22T13:37:27Z" +generated_at: "2026-08-22T16:46:29Z" source: ops-warden/registry/routing/catalog.yaml catalog_revision: "7ce58ae638b89ef07340a4dd72c80ec34fc35c78" catalog_revision_date: "2026-08-22T15:36:37+02:00" -catalog_dirty: false -high_risk_lane_count: 19 +catalog_dirty: true +high_risk_lane_count: 20 concrete_path_count: 14 # Graded high but not a single KV address -- a routing pattern, a broker @@ -25,6 +25,7 @@ no_concrete_path: - inter-hub-bootstrap-ssh - object-storage-sts - openbao-api-key + - openbao-platform-admin-login - ops-warden-warden-sign-token paths: diff --git a/registry/routing/catalog.yaml b/registry/routing/catalog.yaml index 409c2e7..07b2c42 100644 --- a/registry/routing/catalog.yaml +++ b/registry/routing/catalog.yaml @@ -144,6 +144,33 @@ entries: - "For dynamic-lease secrets, revoke the old lease (`bao lease revoke`) instead of a KV put." - "Verify capabilities-safe (`bao token capabilities` on the data path); notify consumers to re-fetch." + - id: openbao-platform-admin-login + title: Attended OpenBao platform administration login + # This is an identity bootstrap, not a secret value lane. The authority it + # establishes is high-risk, but the command prints no token and writes only + # to the caller's normal OpenBao token helper. + risk: high + workload_ref: + applicability: not-applicable + reason: "Attended human operator identity act; the governed admin operation supplies its own resource identity." + need_keywords: [openbao, platform-admin, platform, admin, administrator, first-time, bootstrap, database-engine, database/config, policy, policies, token-role, token-roles, mount, auth-role] + owner_repo: railiance-platform + subsystem: OpenBao operator OIDC via key-cape + warden_executes: false + wiki_ref: wiki/playbooks/openbao-platform-admin-login.md#worker-checklist + canon_ref: railiance-platform/docs/openbao.md + reviewed: "2026-08-22" + status: active + delegation: + mode: native + intended_owner: railiance-platform + reviewed: "2026-08-22" + verified: source-read + auth_method: "attended KeyCape OIDC/MFA at OpenBao auth mount netkingdom, role platform-admin" + fetch_command: "bao login -no-print -method=oidc -path=netkingdom role=platform-admin" + exec_capable: true + lane: login + - id: whynot-design-npm-publish title: whynot-design npm publish token (@whynot/design → coulomb Gitea registry) # Publish rights to the package registry — a leaked token is a supply-chain write, not a read (WARDEN-WP-0032-T05). diff --git a/src/warden/plan.py b/src/warden/plan.py index e59dbd4..639ccfc 100644 --- a/src/warden/plan.py +++ b/src/warden/plan.py @@ -141,6 +141,11 @@ def _autonomous_commands(entry: RouteEntry, domain: Optional[str]) -> List[str]: return cmds expanded = expand_handoff(entry, domain=domain) + if entry.lane == "login": + cmds.append(f"warden access {entry.id} --fetch") + if expanded.fetch_command: + cmds.append(f"# attended owner login: {expanded.fetch_command}") + return cmds if entry.has_native_exec and entry.exec_command: cmds.append(entry.exec_command) if entry.pointer_command: diff --git a/tests/test_plan.py b/tests/test_plan.py index 07c5c2e..447d487 100644 --- a/tests/test_plan.py +++ b/tests/test_plan.py @@ -53,6 +53,33 @@ def test_plan_login_founder_required(): assert plan.founder_act.kind == "oidc_login" +def test_plan_first_time_openbao_database_admin_uses_platform_admin_login(): + plan = build_plan( + "first-time OpenBao database engine administration for " + "database/config/platform-pg-2 dynamic roles policies and token roles; " + "requires attended platform-admin handoff" + ) + assert plan.verdict == "founder_required" + assert plan.lane_id == "openbao-platform-admin-login" + assert plan.founder_act is not None + assert plan.founder_act.kind == "oidc_login" + command = plan.founder_act.details["fetch_command"] + assert command == ( + "bao login -no-print -method=oidc -path=netkingdom role=platform-admin" + ) + assert "financials" not in command + assert "paste_once" not in plan.founder_act.details["desk_hint"] + assert any( + item == "warden access openbao-platform-admin-login --fetch" + for item in plan.commands + ) + assert not any( + flag in item + for item in plan.commands + for flag in ("--exec", "--out", "--wrap") + ) + + def test_plan_unroutable(): # Zero keyword overlap with catalog (avoid tokens like secret/key/token) plan = build_plan("xyzzy-plugh-fnord-qqq-zzzz") diff --git a/tests/test_routing.py b/tests/test_routing.py index ba57b9b..1ad3498 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -160,6 +160,16 @@ def test_generic_and_template_lanes_not_resolvable(): assert catalog.get("key-cape-oidc-login").resolvable is False +def test_platform_admin_login_lane_is_exact_and_non_value_bearing(): + entry = load_catalog(_repo_catalog()).get("openbao-platform-admin-login") + assert entry.lane == "login" + assert entry.risk == "high" + assert entry.fetch_command == ( + "bao login -no-print -method=oidc -path=netkingdom role=platform-admin" + ) + assert entry.workload_ref.resolution == "not-applicable" + + def test_find_exact_id_wins_over_keyword_collision(): catalog = load_catalog(_repo_catalog()) # "npm" alone collides with openbao-api-key; the exact id must resolve uniquely. diff --git a/tests/test_workload_join.py b/tests/test_workload_join.py index 7b6d9e7..2f60fa5 100644 --- a/tests/test_workload_join.py +++ b/tests/test_workload_join.py @@ -14,7 +14,7 @@ def test_repo_catalog_uses_only_explicit_workload_references(): assert report["ok"] is True assert len(report["resolved"]) == 3 assert len(report["unknown"]) == 17 - assert len(report["not_applicable"]) == 7 + assert len(report["not_applicable"]) == 8 assert {row["lane"] for row in report["resolved"]} == { "ops-warden-warden-sign-token", "issue-core-ingestion-api-key", diff --git a/wiki/CredentialRouting.md b/wiki/CredentialRouting.md index f6d4850..3b86c42 100644 --- a/wiki/CredentialRouting.md +++ b/wiki/CredentialRouting.md @@ -90,6 +90,7 @@ lists the interim set — WARDEN-WP-0030). | `ssh-cert-host-access` | **Issues** the cert (`warden sign`) | Use the cert / wire it into `cert_command` | | `ops-warden-warden-sign-token` | "railiance-platform broker owns the `warden-sign` lease — use `credential exec`" | `railiance-platform/scripts/credential.py exec --grant ops-warden/warden-sign` (see playbook) | | `openbao-api-key` | "OpenBao owns this — here is the path/command shape" | Call OpenBao directly, or use `warden access --fetch/--exec` as yourself when the lane is `exec_capable` | +| `openbao-platform-admin-login` | "This is an attended OpenBao administration identity act, not workload secret retrieval" | Use KeyCape-backed OIDC/MFA at `auth/netkingdom`, role `platform-admin`; never substitute a workload role or root token | | `flex-auth-policy-check` | "flex-auth decides — here is the policy doc" | Query flex-auth / embed the PEP | | `key-cape-oidc-login` | "key-cape / Keycloak owns identity" | Authenticate via IAM Profile, or use the `warden access` login lane as yourself | | `ops-bridge-tunnel` | "ops-bridge owns transport — supply a `cert_command`" | Open the tunnel with ops-bridge | diff --git a/wiki/playbooks/openbao-platform-admin-login.md b/wiki/playbooks/openbao-platform-admin-login.md new file mode 100644 index 0000000..a5c5322 --- /dev/null +++ b/wiki/playbooks/openbao-platform-admin-login.md @@ -0,0 +1,53 @@ +# OpenBao platform-admin login + +## Worker checklist + +Use this lane only for an attended OpenBao control-plane operation whose +reviewed procedure requires `platform-admin`, such as configuring a database +secrets-engine connection, policies, auth roles, or token roles. It is not a +workload KV-read lane and it does not provision a secret value. + +1. Plan the exact administration need before drafting any operator step: + + ```bash + warden plan "attended OpenBao platform administration for " --json + ``` + + The result must select `openbao-platform-admin-login`, return + `founder_required`, and name one `oidc_login` act. If it selects + `openbao-api-key`, a workload role, paste-once provisioning, or root, stop and + report a routing defect. + +2. The operator performs the one identity act through KeyCape OIDC/MFA: + + ```bash + bao login -no-print -method=oidc -path=netkingdom role=platform-admin + ``` + + `-no-print` is mandatory. Do not paste a token into chat, State Hub, a shell + argument, or a temporary handoff file. Root is offline break-glass authority, + not a fallback for an OIDC or callback failure. + +3. Verify authority using metadata or capabilities only, never by reading a + secret value. Then run only the separately reviewed owner procedure. For the + database engine this procedure lives in `rapp-postgres`; the login does not + itself approve configuration changes. + +4. Revoke the attended token when the reviewed operation and its non-secret + verification are complete: + + ```bash + bao token revoke -self + ``` + +If browser login fails before authentication, confirm the `netkingdom` auth +mount, `platform-admin` role, and allowed callback with `railiance-platform` and +`key-cape`. Do not retry with a workload-specific OIDC role: it is intentionally +incapable of OpenBao control-plane administration. + +## Authority + +- OpenBao policy and role owner: `railiance-platform/docs/openbao.md` +- Human identity and MFA provider: key-cape / Keycloak +- Database-engine procedure owner: `rapp-postgres` +- Routing decision and founder-act surface: WARDEN-WP-0029 diff --git a/workplans/WARDEN-WP-0033-native-lane-handoff.md b/workplans/WARDEN-WP-0033-native-lane-handoff.md index a8d56eb..18154e1 100644 --- a/workplans/WARDEN-WP-0033-native-lane-handoff.md +++ b/workplans/WARDEN-WP-0033-native-lane-handoff.md @@ -11,7 +11,7 @@ planning_priority: P1 depends_on_workplans: - WARDEN-WP-0030 created: "2026-08-21" -updated: "2026-08-21" +updated: "2026-08-22" --- # WARDEN-WP-0033 — Native lane handoff @@ -248,6 +248,46 @@ no code change, punishing whoever commits next rather than whoever owns the blocker. The CI test is structural instead — every interim lane must record *how* it was verified, which fails on the commit that introduces the omission. +```task +id: WARDEN-WP-0033-T06 +status: done +priority: high +``` + +**Correct first-time OpenBao administration routing.** `rapp-postgres` reported +on 2026-08-22 that `warden plan` mapped first-time +`database/config/platform-pg-2` administration to the generic +`openbao-api-key` template. It consequently proposed paste-once KV provisioning +and a workload OIDC role, neither of which can configure database connections, +policies, auth roles, or token roles. Their Desk session stopped without an +approval or secret action. + +The authoritative platform runbook and rapp-postgres's live T04 evidence agree +on the missing distinction: this is one attended KeyCape-backed OpenBao identity +act at auth mount `netkingdom`, role `platform-admin`. It is not a credential +value handoff. Root stays offline break-glass authority and a workload role must +never be widened to make the operation pass. + +Add an exact `openbao-platform-admin-login` lane, an operator playbook, and a +regression that makes the reported request resolve to `founder_required` with +`oidc_login` — never `paste_once_provision`. Verify the route, then return the +corrected non-secret command to rapp-postgres. + +**Done 2026-08-22.** The new active lane resolves the exact reported request at +score 20, ahead of the generic KV template at 11, and returns one +`founder_required` act: +`bao login -no-print -method=oidc -path=netkingdom role=platform-admin`. +Planner output offers only `warden access openbao-platform-admin-login --fetch` +and the exact owner command; login lanes no longer inherit secret-value +transports (`--exec`, `--out`, `--wrap`). The lane is explicitly a high-risk, +non-workload human identity act, so the generated boundary artifact lists it as +having no KV address rather than inventing one. + +Verification: 384 tests pass, Ruff passes, workload accounting resolves all 28 +lanes (3 resolved, 17 unknown, 8 not-applicable), and the boundary invariant +reports 20 high-risk lanes, 14 concrete paths covered, 6 non-KV/pattern lanes, +0 uncovered. No login or credential action was performed. + ## Related - `secrets-engine` `SECRETS-WP-0006` — catalog admission, decision `ae676382`