generated from coulomb/repo-seed
Adds transparent, policy-gated, audited proxy of a non-SSH credential through `warden access`, for exec_capable lanes. Three guardrails in code: - G1 caller identity: runs the owner's tool with the caller's own env; warden injects no token of its own (caller_auth_present check). - G2 transit-only: --fetch inherits stdout (never PIPE) so the value never enters warden's memory or any log; --exec injects into the child env only. Audit (access-audit.log) is metadata-only. - G3 policy gate: check_fetch_policy runs before any fetch; with policy.enabled=false the proxy refuses unless --no-policy is given. resolve_fetch_command refuses unresolved <…> placeholders rather than guess owner-side names. New warden/proxy.py + policy.check_fetch_policy; tests/test_proxy.py asserts all three guardrails. 168 passed, lint clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
207 lines
9.3 KiB
Markdown
207 lines
9.3 KiB
Markdown
---
|
||
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 <need> [--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 `<domain>` 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 <need> --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 <need> --exec -- <cmd>` — 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 <need>` 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`
|