Files
ops-warden/workplans/WARDEN-WP-0014-operator-access-assist.md
tegwick 6dfa69e310 feat(WARDEN-WP-0014): T3 — OpenBao proxy lane (--fetch / --exec)
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>
2026-06-27 16:26:03 +02:00

9.3 KiB
Raw Blame History

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.

  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

id: WARDEN-WP-0014-T01
status: done
priority: high
state_hub_task_id: "abb0e722-6524-4224-8638-6ee1573ed3e0"
  • 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.)
  • Fields are structured pointers/templates, not prose restatements — each sits alongside the owner's canon_ref for the authoritative procedure (no drift).
  • Populate for openbao-api-key (covers the coulomb_social npm shape: keyword npm_auth_token added) as the reference example; draft entries untouched.
  • 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

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 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.)
  • Advisory is the default behavior (no value fetched); SSH lane points at warden sign; routed lanes end with "warden advises, the owner vends".
  • --json output for agentic operators — stable, secret-free shape (handoff block + next_action); --domain substitutes <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; --field names the env var (e.g. NPM_AUTH_TOKEN).
  • Guardrails G1G3 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.
  • Metadata-only audit per call (write_auditstate_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 access to orchestrate the key-cape/Keycloak OIDC login flow (interactive tool hand-off) under the same advisory/proxy split.
  • Login lane respects G1G3; 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 — 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