--- id: WARDEN-WP-0014 type: workplan title: "Operator Access Assist — warden access front door" domain: infotech repo: ops-warden status: active owner: codex topic_slug: custodian planning_priority: high planning_order: 14 created: "2026-06-27" updated: "2026-06-27" state_hub_workstream_id: "3c30b2ed-6ede-4b95-a438-fde6da6f6633" --- # WARDEN-WP-0014 — Operator Access Assist (`warden access`) **Scope:** Make ops-warden the consistent operator-facing front door for **every** NetKingdom security/access need — not just the SSH lane. Add a `warden access` command surface that (a) advises: emits the auth method, path, policy context, and exact command skeleton for any credential need, and (b) **proxies**: transparently fetches the value from the owning subsystem *as the caller* and streams it to the operator's destination, **without ops-warden ever persisting, caching, or logging the secret, and without ops-warden holding any standing privileged credential.** Centralize **knowledge and policy** in ops-warden; leave **custody and execution detail** in the owning subsystems (OpenBao, key-cape, flex-auth). ops-warden becomes a transparent, policy-gated, audited conduit — `vault exec` / `op run` shaped — never a standing secret broker. **Charter note:** This extends the WP-0010 routing charter from a *pointer layer* ("who owns it") to an *assist layer* ("here is exactly how to get it, gated and audited"). It does **not** move custody into ops-warden. See the three non-negotiable guardrails below — they are the line between a transparent conduit (sanctioned) and a standing broker/honeypot (forbidden). **Out of scope:** - ops-warden holding a long-lived OpenBao/secret-read token of its own. - Persisting, caching, or logging any secret **value** anywhere (disk, log, hub, git). - Creating OpenBao paths or policies (coordinate with railiance-platform / flex-auth). - Restating an owner's procedure as prose in the catalog (reference canon, don't copy). - Identity/MFA implementation (key-cape owns it; we orchestrate its CLI only). **Depends on:** WP-0010 (charter + catalog schema), WP-0011 (`warden route` CLI), WP-0007/0009 (flex-auth policy gate — reused as the fetch-path gate). **Status:** `proposed` — awaiting Bernd's review before implementation. --- ## Three non-negotiable guardrails (acceptance-blocking) These are the design invariants that keep proxy mode safe. Any task that violates one is rejected regardless of convenience. 1. **Operator identity, never warden's.** `--fetch`/`--exec` authenticate as the *caller* (their OIDC/OpenBao token, the agent's own auth). ops-warden carries **no** standing secret-read credential. If the caller has no valid auth, the command fails with a routing pointer to the auth step — it does not fall back to a warden token. 2. **Transit only — no persistence, no logging of values.** The secret flows subsystem → caller destination (env of a child process, or stdout) via a streamed `exec`. ops-warden must not buffer it to disk, cache it, echo it to logs, or write it to the State Hub. Audit records **metadata only** (who, need id, owner path, time, policy decision id) — never the value. 3. **Policy gate on the fetch path.** `--fetch`/`--exec` run the flex-auth check **before** proxying (reusing the WP-0007 gate). When `policy.enabled: false`, fetch is **advisory-only** by default and requires an explicit `--no-policy` acknowledgement to proxy ungated — surfaced loudly in output and audit. --- ## Phasing decision (default — adjust in review) OpenBao lane **first** (covers the immediate npm/API/DB need), key-cape/login lane in a later task within the same WP. Rationale: OpenBao KV is the highest-frequency operator need and the one this conversation surfaced; login flows are a thinner orchestration of an interactive tool and lower risk to defer. --- ## Tasks ### T1 — Catalog schema: structured handoff fields ```task id: WARDEN-WP-0014-T01 status: done priority: high state_hub_task_id: "abb0e722-6524-4224-8638-6ee1573ed3e0" ``` - [x] Extend `registry/routing/catalog.yaml` entry schema with optional structured handoff fields for non-SSH lanes: `auth_method`, `path_template`, `fetch_command`, `exec_capable` (bool), `policy_ref`. (`RouteEntry` + `_parse_entry`; `has_handoff` helper.) - [x] Fields are **structured pointers/templates**, not prose restatements — each sits alongside the owner's `canon_ref` for the authoritative procedure (no drift). - [x] Populate for `openbao-api-key` (covers the coulomb_social npm shape: keyword `npm_auth_token` added) as the reference example; `draft` entries untouched. - [x] Validation: `_assert_no_secret_material` rejects known token prefixes and high-entropy runs in any handoff field; `exec_capable` requires `fetch_command`. Tests in `tests/test_routing.py` (handoff parse, real-catalog, secret-leak matrix, placeholder-accepted). ### T2 — `warden access` advisory surface ```task id: WARDEN-WP-0014-T02 status: done priority: high state_hub_task_id: "c1497263-7124-459f-b63a-d0c0c7005c86" ``` - [x] `warden access [--domain X] [--json]` — resolves via the same matcher as `warden route find` and renders the **structured handoff**: owner, auth method, path template, command skeleton, policy ref + gate status, proxy hint, and the `<…>` owner-confirmed-name note. (`warden/access.py` pure module + `access` command in `cli.py`.) - [x] Advisory is the **default** behavior (no value fetched); SSH lane points at `warden sign`; routed lanes end with "warden advises, the owner vends". - [x] `--json` output for agentic operators — stable, secret-free shape (`handoff` block + `next_action`); `--domain` substitutes `` only. - [x] Tests: `tests/test_access.py` (expansion, gate status, advisory/SSH/JSON/no-match). ### T3 — OpenBao proxy lane (`--fetch` / `--exec`) ```task id: WARDEN-WP-0014-T03 status: done priority: high state_hub_task_id: "6d3eb0e4-309c-4065-893e-6c4053fb0db2" ``` - [x] `warden access --fetch` — policy-gate (G3) → run the owning tool (`bao kv get ...`) **as the caller** with **inherited stdout** → value streams to stdout and never enters warden's memory (`proxy_fetch`). No buffering, no log. - [x] `warden access --exec -- ` — runs the child with the secret injected into *its* env only (`proxy_exec`); value never lands in the caller's shell env; `--field` names the env var (e.g. `NPM_AUTH_TOKEN`). - [x] Guardrails G1–G3 in code (`warden/proxy.py`, `_access_proxy` in `cli.py`): G1 caller token only (no warden credential; `caller_auth_present`); G2 transit-only (inherit-stdout fetch; no disk/log write); G3 `check_fetch_policy` before any exec, `--no-policy` required to proxy ungated. `tests/test_proxy.py` asserts all three, plus `resolve_fetch_command` refuses unresolved `<…>` placeholders. Live smoke against a fake `bao` confirmed gate-refusal, stream, exec-inject, and a secret-free audit log. - [x] Metadata-only audit per call (`write_audit` → `state_dir/access-audit.log`). ### T4 — key-cape / login orchestration lane ```task id: WARDEN-WP-0014-T04 status: todo priority: medium state_hub_task_id: "481997e4-193d-4724-84a6-61cbc2940153" ``` - [ ] Extend `warden access` to orchestrate the key-cape/Keycloak OIDC login flow (interactive tool hand-off) under the same advisory/proxy split. - [ ] Login lane respects G1–G3; no token caching by warden. ### T5 — Docs, security model, and INTENT/SCOPE alignment ```task id: WARDEN-WP-0014-T05 status: todo priority: medium state_hub_task_id: "a5eb616e-4edf-42db-a4fb-bf296cdb92bc" ``` - [ ] `wiki/OperatorAccessAssist.md` — the `warden access` contract, the conduit-vs-broker boundary, and the three guardrails as a security model statement. - [ ] Update `wiki/AccessRouting.md` (issue/route/**assist** roles), `CredentialRouting.md`, and the `credential-routing.md` agent rule (new anti-pattern: "warden as standing broker" is forbidden; transparent `--fetch` is sanctioned). - [ ] SCOPE/INTENT: record the charter extension from pointer-layer to assist-layer and bump the maturity vector (A4 → A5 candidate on Availability). - [ ] `history/2026-06-27-operator-access-assist-charter.md` — decision record for the proxy-mode choice and its guardrails. --- ## Acceptance - `warden access ` advises for any catalog need; `--fetch`/`--exec` proxy the OpenBao lane end to end against a real KV path. - All three guardrails hold under test: **no** secret value touches disk/log/hub/git; ops-warden holds **no** standing secret-read credential; the policy gate runs **before** every fetch. - Catalog carries structured handoff fields that reference (never restate) owner canon. - Docs state the conduit-vs-broker boundary explicitly; the agent rule forbids the broker pattern. - No secret material anywhere in code, catalog, docs, logs, or tests. --- ## See also - `WARDEN-WP-0010` (routing charter), `WARDEN-WP-0011` (`warden route` CLI) - `WARDEN-WP-0007` / `WARDEN-WP-0009` (flex-auth policy gate — reused as fetch gate) - `wiki/AccessRouting.md`, `wiki/CredentialRouting.md`, `wiki/PolicyGatedSigning.md` - `.claude/rules/credential-routing.md` - `history/2026-06-24-intent-scope-gap-analysis.md`