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>
9.3 KiB
id, type, title, domain, repo, status, owner, topic_slug, planning_priority, planning_order, created, updated, state_hub_workstream_id
| id | type | title | domain | repo | status | owner | topic_slug | planning_priority | planning_order | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| WARDEN-WP-0014 | workplan | Operator Access Assist — warden access front door | infotech | ops-warden | active | codex | custodian | high | 14 | 2026-06-27 | 2026-06-27 | 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.
-
Operator identity, never warden's.
--fetch/--execauthenticate 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. -
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. -
Policy gate on the fetch path.
--fetch/--execrun the flex-auth check before proxying (reusing the WP-0007 gate). Whenpolicy.enabled: false, fetch is advisory-only by default and requires an explicit--no-policyacknowledgement 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
id: WARDEN-WP-0014-T01
status: done
priority: high
state_hub_task_id: "abb0e722-6524-4224-8638-6ee1573ed3e0"
- Extend
registry/routing/catalog.yamlentry 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_handoffhelper.) - Fields are structured pointers/templates, not prose restatements — each
sits alongside the owner's
canon_reffor the authoritative procedure (no drift). - Populate for
openbao-api-key(covers the coulomb_social npm shape: keywordnpm_auth_tokenadded) as the reference example;draftentries untouched. - Validation:
_assert_no_secret_materialrejects known token prefixes and high-entropy runs in any handoff field;exec_capablerequiresfetch_command. Tests intests/test_routing.py(handoff parse, real-catalog, secret-leak matrix, placeholder-accepted).
T2 — warden access advisory surface
id: WARDEN-WP-0014-T02
status: done
priority: high
state_hub_task_id: "c1497263-7124-459f-b63a-d0c0c7005c86"
warden access <need> [--domain X] [--json]— resolves via the same matcher aswarden route findand 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.pypure module +accesscommand incli.py.)- Advisory is the default behavior (no value fetched); SSH lane points at
warden sign; routed lanes end with "warden advises, the owner vends". --jsonoutput for agentic operators — stable, secret-free shape (handoffblock +next_action);--domainsubstitutes<domain>only.- Tests:
tests/test_access.py(expansion, gate status, advisory/SSH/JSON/no-match).
T3 — OpenBao proxy lane (--fetch / --exec)
id: WARDEN-WP-0014-T03
status: done
priority: high
state_hub_task_id: "6d3eb0e4-309c-4065-893e-6c4053fb0db2"
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.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;--fieldnames the env var (e.g.NPM_AUTH_TOKEN).- Guardrails G1–G3 in code (
warden/proxy.py,_access_proxyincli.py): G1 caller token only (no warden credential;caller_auth_present); G2 transit-only (inherit-stdout fetch; no disk/log write); G3check_fetch_policybefore any exec,--no-policyrequired to proxy ungated.tests/test_proxy.pyasserts all three, plusresolve_fetch_commandrefuses unresolved<…>placeholders. Live smoke against a fakebaoconfirmed gate-refusal, stream, exec-inject, and a secret-free audit log. - Metadata-only audit per call (
write_audit→state_dir/access-audit.log).
T4 — key-cape / login orchestration lane
id: WARDEN-WP-0014-T04
status: todo
priority: medium
state_hub_task_id: "481997e4-193d-4724-84a6-61cbc2940153"
- Extend
warden accessto 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
id: WARDEN-WP-0014-T05
status: todo
priority: medium
state_hub_task_id: "a5eb616e-4edf-42db-a4fb-bf296cdb92bc"
wiki/OperatorAccessAssist.md— thewarden accesscontract, 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 thecredential-routing.mdagent rule (new anti-pattern: "warden as standing broker" is forbidden; transparent--fetchis 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/--execproxy 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 routeCLI)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.mdhistory/2026-06-24-intent-scope-gap-analysis.md