feat(WARDEN-WP-0019): route secret-exec lanes to secrets-engine (route-primary, proxy fallback)

secrets-engine (SECRETS-WP-0003) shipped a native secret-exec front door
(`secrets-engine route/exec`, decision e6381a56) and asked ops-warden to route to it.
Bernd's call: route-primary, proxy-fallback — surface the secrets-engine exec as the
primary path for owned lanes, keep `warden access --exec` as a transparent fallback.

T1 — RouteEntry gains exec_owner/exec_command/pointer_command (+ has_native_exec),
screened for secret material like the other handoff fields. whynot-design-npm-publish
points its native exec at secrets-engine. `warden access` renders Primary (secrets-engine
exec) + Fallback (warden proxy); route/access JSON gain the fields and a native-exec-aware
next_action. Tests added; 217 pass, lint clean.

T2 — credential-routing.md adds secrets-engine as the secret-exec owner (route primary,
proxy fallback); SCOPE adds secrets-engine to Related Repos and records the npm lane as
production-exercised (@whynot/design@0.4.0); playbook leads with secrets-engine exec and
fixes the fallback one-liner (--field NPM_AUTH_TOKEN, --no-policy) per whynot-design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 17:41:49 +02:00
parent d003f0ca4d
commit bd335ec724
10 changed files with 223 additions and 17 deletions

View File

@@ -40,12 +40,17 @@ Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run wa
| I need… | Owner | ops-warden role |
| --- | --- | --- |
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Issue**`warden sign` |
| API key, DB password, provider token | OpenBao (`railiance-platform`) | **Assist** `warden access <need> --fetch/--exec` proxies as you; OpenBao keeps custody |
| Provisioned secret-exec lane (e.g. npm publish) | **secrets-engine** | **Route** — primary is `secrets-engine exec --catalog <id> -- <cmd>`; `warden access <id> --exec` is the transparent fallback |
| Generic API key / DB password / provider token | OpenBao (`railiance-platform`) | **Assist**`warden access <need> --fetch/--exec` proxies as you; OpenBao keeps custody |
| Login / OIDC / MFA | key-cape / Keycloak | **Assist**`warden access <need> --fetch` runs the login as you |
| Authorization decision | flex-auth | Route only |
| activity-core → issue-core emission | activity-core + issue-core | Route — `warden route show activity-core-issue-sink` |
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | Route only |
For an owned lane, `warden route find <need> --json` / `warden access <id>` surface
`exec_owner`, the `secrets-engine exec` command, and the `resolvable` flag. Run the
secrets-engine command; ops-warden routes to it and requests/holds no token.
### Anti-patterns (do not do these)
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.

View File

@@ -236,6 +236,10 @@ repos' lanes (see Known gaps).
conformance checker, dev doubles); canon landing owner-driven
- **ops-bridge cert_command:** WP-0016 shipped to pilot-ready (readiness gate +
offline contract smoke + handoff); live cutover is ops-bridge's
- **Access front door:** WP-0017 discoverability + WP-0018 first concrete lane
(`whynot-design-npm-publish`), **production-exercised** — whynot-design published
`@whynot/design@0.4.0` through the conduit. WP-0019 routes provisioned secret-exec
lanes to **secrets-engine** (`secrets-engine exec`), proxy as transparent fallback
- **Active work:** none open in ops-warden; remaining distance is other repos' lanes
- **Integration docs:** cert_command migration, token hygiene, principals drift (`wiki/playbooks/`)
- **Latest assessment:** `history/2026-06-24-intent-scope-gap-analysis.md`
@@ -284,6 +288,7 @@ Downstream: `ops-bridge` (primary), kaizen agents, CI automations, human operato
| `railiance-platform` | OpenBao deployment and platform secrets |
| `flex-auth` | Authorization; policy package shipped (FLEX-WP-0006); runtime deploy FLEX-WP-0007 |
| `key-cape` | Identity / IAM Profile lightweight mode |
| `secrets-engine` | Owner-native secret-exec front door (`secrets-engine exec/route`); ops-warden routes provisioned secret lanes to it (WP-0019) |
| `state-hub` | Workstream registry |
---

View File

@@ -89,6 +89,12 @@ entries:
policy_ref: "flex-auth check secret.read:whynot-design"
exec_capable: true
lane: secret
# Owner-native exec front door (WP-0019, secrets-engine SECRETS-WP-0003, decision
# e6381a56): route-primary, proxy-fallback. The secrets-engine exec is the primary
# path; warden access --fetch/--exec remains a transparent fallback.
exec_owner: secrets-engine
exec_command: "secrets-engine exec --catalog whynot-design-npm-publish -- <cmd>"
pointer_command: "secrets-engine route whynot-design-npm-publish --json"
- id: flex-auth-policy-check
title: Authorization decision — may this actor perform this action

View File

@@ -556,6 +556,17 @@ def _entry_summary(entry) -> dict:
# resolvable: can `warden access --fetch` run this now with no <…> to fill?
# Lets an automated caller gate on readiness before attempting a fetch.
"resolvable": entry.resolvable,
# Owner-native exec front door (WP-0019): when present, this subsystem's exec is
# the PRIMARY path; ops-warden's proxy is the transparent fallback.
**(
{
"exec_owner": entry.exec_owner,
"exec_command": entry.exec_command,
"pointer_command": entry.pointer_command,
}
if entry.has_native_exec
else {}
),
"wiki_ref": entry.wiki_ref,
"canon_ref": entry.canon_ref,
"reviewed": entry.reviewed,
@@ -677,6 +688,11 @@ def route_show(
if entry.warden_executes:
summary["steps"] = entry.steps
summary["cert_command"] = entry.cert_command
elif entry.has_native_exec:
summary["next_action"] = (
f"primary: run via {entry.exec_owner} — `{entry.exec_command}`; ops-warden "
f"routes to the owner (fallback: `warden access <need> --exec`). See `{entry.wiki_ref}`."
)
elif entry.exec_capable:
summary["next_action"] = (
f"ops-warden can proxy this as the caller: `warden access <need> --fetch`"
@@ -756,6 +772,12 @@ def _access_json(entry, expanded, gate: str, domain: Optional[str]) -> dict:
if entry.warden_executes:
payload["next_action"] = "ops-warden issues this directly — see cert_command"
payload["cert_command"] = entry.cert_command
elif entry.has_native_exec:
payload["next_action"] = (
f"primary: run via {entry.exec_owner} — `{entry.exec_command}`; "
"ops-warden routes to the owner (fallback: `warden access <need> --exec`). "
"ops-warden holds no token."
)
elif expanded.exec_capable:
verb = "fetch" if entry.lane != "login" else "login"
payload["next_action"] = (
@@ -994,22 +1016,39 @@ def access(
console.print(f" wiki : {entry.wiki_ref}")
console.print(f" canon : {entry.canon_ref}")
if expanded.exec_capable:
proxy = f"warden access {need!r}"
if domain:
proxy += f" --domain {domain}"
hint = (
"add --fetch to proxy as the caller"
if entry.lane != "login"
else "add --fetch to run the interactive login as the caller"
proxy = f"warden access {need!r}"
if domain:
proxy += f" --domain {domain}"
if entry.has_native_exec:
console.print(
f" exec : [bold]{entry.exec_command}[/bold] "
f"[cyan](via {entry.exec_owner} — primary)[/cyan]"
)
console.print(f" proxy : [dim]{proxy} --fetch[/dim] [yellow]({hint})[/yellow]")
if entry.pointer_command:
console.print(f" pointer : [dim]{entry.pointer_command}[/dim]")
if expanded.exec_capable:
label = "fallback" if entry.has_native_exec else "proxy"
hint = (
"transparent conduit — fetches as you"
if entry.lane != "login"
else "runs the interactive login as you"
)
console.print(f" {label:<8} : [dim]{proxy} --fetch[/dim] [yellow]({hint})[/yellow]")
if expanded.path_template and "<" in expanded.path_template:
console.print(
" note : remaining <…> placeholders are owner-confirmed names "
f"(coordinate with {entry.owner_repo})."
)
if expanded.exec_capable:
if entry.has_native_exec:
console.print(
f"\n[green]Primary:[/green] run it via [bold]{entry.exec_owner}[/bold] — "
f"[bold]{entry.exec_command}[/bold]. ops-warden routes to the owner and holds no token.\n"
f"[dim]Fallback:[/dim] [bold]{proxy} --exec -- <cmd>[/bold] — ops-warden's transparent "
"conduit (runs the fetch as you, holds nothing)."
)
elif expanded.exec_capable:
verb = "fetch this for you" if entry.lane != "login" else "run this login for you"
console.print(
f"\n[green]ops-warden can {verb}[/green] as the caller — "

View File

@@ -26,7 +26,11 @@ from warden.routing.models import RouteEntry
# Structured handoff string fields (WP-0014) — templates and pointers only.
# Every one is scanned for accidental secret material; see _assert_no_secret_material.
_HANDOFF_STR_FIELDS = ("auth_method", "path_template", "fetch_command", "policy_ref")
_HANDOFF_STR_FIELDS = (
"auth_method", "path_template", "fetch_command", "policy_ref",
# Owner-native exec front door (WP-0019) — pointer commands, screened too.
"exec_command", "pointer_command",
)
# Known secret-bearing token prefixes — a literal here means a value leaked into
# the catalog (which is git-tracked and agent-visible). Templates use `<...>`.
@@ -265,6 +269,9 @@ def _parse_entry(raw: dict, index: int) -> RouteEntry:
exec_capable=exec_capable,
policy_ref=handoff["policy_ref"],
lane=lane,
exec_owner=str(raw["exec_owner"]) if raw.get("exec_owner") else None,
exec_command=handoff["exec_command"],
pointer_command=handoff["pointer_command"],
)

View File

@@ -43,11 +43,23 @@ class RouteEntry:
# no identity yet), no caller-auth precheck (the point is to get one),
# run interactively as the caller; warden never captures the token.
lane: str = "secret"
# Owner-native exec front door (WP-0019). When `exec_owner` is set, that subsystem
# (e.g. secrets-engine) provides the PRIMARY way to run a secret-backed command; the
# catalog routes to it and keeps ops-warden's own --fetch/--exec proxy as a transparent
# fallback (route-primary, proxy-fallback). Pointers/templates only — never a value.
exec_owner: Optional[str] = None # subsystem owning the native exec (e.g. secrets-engine)
exec_command: Optional[str] = None # e.g. "secrets-engine exec --catalog <id> -- <cmd>"
pointer_command: Optional[str] = None # e.g. "secrets-engine route <id> --json"
@property
def is_active(self) -> bool:
return self.status == "active"
@property
def has_native_exec(self) -> bool:
"""True when an owner-native exec front door is the primary path for this lane."""
return bool(self.exec_owner and self.exec_command)
@property
def has_handoff(self) -> bool:
"""True when structured assist fields are present (advisory richness)."""

View File

@@ -77,6 +77,15 @@ def test_access_advisory_output(monkeypatch):
assert "never holds" in r.stdout
def test_access_native_exec_shows_primary_and_fallback(monkeypatch):
"""A secrets-engine-owned lane leads with the native exec; proxy is the fallback."""
monkeypatch.setenv("WARDEN_ROUTING_CATALOG", str(_repo_catalog()))
r = runner.invoke(app, ["access", "whynot-design-npm-publish"])
assert r.exit_code == 0
assert "secrets-engine exec --catalog whynot-design-npm-publish" in r.stdout
assert "Primary" in r.stdout and "Fallback" in r.stdout
def test_access_route_only_lane_says_owner_vends(monkeypatch):
"""A non-exec lane (host principal deploy) keeps the advise-only framing."""
monkeypatch.setenv("WARDEN_ROUTING_CATALOG", str(_repo_catalog()))

View File

@@ -99,6 +99,32 @@ def test_find_exact_id_wins_over_keyword_collision():
assert catalog.find("whynot-design-npm-publish", limit=1)[0].id == "whynot-design-npm-publish"
def test_native_exec_owner_on_npm_lane():
"""secrets-engine is the owner-native exec front door for the npm lane (WP-0019)."""
catalog = load_catalog(_repo_catalog())
e = catalog.get("whynot-design-npm-publish")
assert e.has_native_exec is True
assert e.exec_owner == "secrets-engine"
assert "secrets-engine exec --catalog whynot-design-npm-publish" in e.exec_command
assert "secrets-engine route" in e.pointer_command
# The proxy fallback is still available (exec_capable + resolvable).
assert e.exec_capable is True and e.resolvable is True
def test_lanes_without_native_exec():
catalog = load_catalog(_repo_catalog())
assert catalog.get("openbao-api-key").has_native_exec is False
assert catalog.get("ssh-cert-host-access").has_native_exec is False
def test_cli_show_native_exec_json(repo_catalog_env):
result = runner.invoke(app, ["route", "show", "whynot-design-npm-publish", "--json"])
data = json.loads(result.stdout)
assert data["exec_owner"] == "secrets-engine"
assert "secrets-engine exec" in data["exec_command"]
assert "primary" in data["next_action"] and "secrets-engine" in data["next_action"]
def test_no_double_source_rule_rejects_routed_steps(tmp_path):
bad = dict(ROUTED_ENTRY)
bad["steps"] = ["do a thing on OpenBao"] # non-SSH entry must not carry steps

View File

@@ -38,13 +38,24 @@ this token** — it is the access front door: `warden access` proxies the read f
Your token must carry the `whynot-design` group bound claim; a non-whynot identity is
denied by policy (verified negative case).
2. **Fetch or run via the front door** — keyed by the stable catalog id, zero placeholders:
2. **Run via the owner-native front door (primary).** secrets-engine owns the secret-exec
for this lane (SECRETS-WP-0003, decision e6381a56); ops-warden routes to it:
```bash
warden access whynot-design-npm-publish --fetch # stream the token to you
warden access whynot-design-npm-publish --exec -- npm publish # inject into the child only
secrets-engine route whynot-design-npm-publish --json # pointer / readiness
secrets-engine exec --catalog whynot-design-npm-publish -- npm publish
```
The value transits to you (or the child env) and never enters ops-warden's memory, disk,
or audit log (metadata-only audit).
**ops-warden transparent fallback** — same lane via the `warden access` proxy (fetches as
you, holds nothing). Field-verified flags (whynot-design, @whynot/design@0.4.0):
```bash
# --exec needs the env-var name; --no-policy is required while the gate is advisory
# (policy.enabled=false), else the call exits 4.
warden access whynot-design-npm-publish --no-policy --field NPM_AUTH_TOKEN \
--exec -- npm publish
warden access whynot-design-npm-publish --no-policy --field NPM_AUTH_TOKEN --fetch
```
On either path the value transits to you (or the child env) and never enters
ops-warden's memory, disk, or audit log.
3. **Readiness gate (for automated callers).** Before attempting `--fetch`, check the flag:
```bash

View File

@@ -0,0 +1,86 @@
---
id: WARDEN-WP-0019
type: workplan
title: "Route secret-exec lanes to secrets-engine (route-primary, proxy fallback)"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 19
created: "2026-06-29"
updated: "2026-06-29"
---
# WARDEN-WP-0019 — Route secret-exec lanes to secrets-engine
**Trigger:** secrets-engine (SECRETS-WP-0003, msg 765a03f0) shipped a native secret-exec
front door — `secrets-engine route <id> --json` and `secrets-engine exec --catalog <id> --
<cmd>` with canonical decision ids — and asked ops-warden to **route to it**. This is the
owner-native execution lane that ops-warden's `warden access --exec` proxy was filling as a
stopgap (WP-0014). whynot-design already published `@whynot/design@0.4.0` through the proxy
on this same lane, so both paths resolve today.
**Decision (Bernd, 2026-06-29): route-primary, proxy-fallback.** For lanes secrets-engine
owns, ops-warden surfaces `secrets-engine exec/route` as the **primary** path and keeps its
own `warden access --exec` as a documented **transparent fallback**. ops-warden stays the
discovery front door; secrets-engine is the exec owner. Boundary unchanged: ops-warden holds
or stores no token on either path.
**Out of scope:** ops-warden invoking `secrets-engine exec` itself (it routes/points, the
caller runs it); changing the proxy's security model; the production policy-gate flip.
---
## Tasks
### T1 — Catalog + CLI: surface the owner-native exec front door
```task
id: WARDEN-WP-0019-T01
status: done
priority: high
```
- [x] `RouteEntry` gains `exec_owner` / `exec_command` / `pointer_command` (pointers only,
screened by `_assert_no_secret_material`) and a `has_native_exec` property.
- [x] `whynot-design-npm-publish` entry: `exec_owner: secrets-engine`,
`exec_command: secrets-engine exec --catalog whynot-design-npm-publish -- <cmd>`,
`pointer_command: secrets-engine route whynot-design-npm-publish --json`. Keep the
existing `fetch_command`/`exec_capable` (the proxy fallback).
- [x] `warden access`: when `exec_owner` is set, render the secrets-engine exec as the
**primary** line and the `warden access --exec` proxy as the **fallback**; JSON gains
`exec_owner`/`exec_command`/`pointer_command`. `route find/show` JSON too.
- [x] Tests in `tests/test_routing.py` / `tests/test_access.py`.
### T2 — Agent rule, SCOPE, playbook
```task
id: WARDEN-WP-0019-T02
status: done
priority: medium
```
- [x] `.claude/rules/credential-routing.md`: add secrets-engine as the secret-exec owner;
for OpenBao-backed secret lanes the route is "secrets-engine `exec` (primary),
ops-warden `warden access --exec` (transparent fallback)".
- [x] SCOPE: add secrets-engine to Related Repos + the routing model; note the
whynot-design lane is **production-exercised** (real 0.4.0 publish), not just resolvable.
- [x] `wiki/playbooks/whynot-design-npm-publish.md`: lead with the secrets-engine exec
command; fix the fallback one-liner per whynot-design's field notes
(`--field NPM_AUTH_TOKEN`, and `--no-policy` while `policy.enabled=false`).
---
## Acceptance
- `warden access whynot-design-npm-publish` shows the secrets-engine exec as primary and the
warden proxy as fallback; `--json` carries `exec_owner`/`exec_command`.
- The credential-routing rule names secrets-engine as the secret-exec owner.
- No secret material anywhere; ops-warden holds no token on either path.
## See also
- secrets-engine SECRETS-WP-0003, decision e6381a56, `docs/whynot-design-real-publish-closeout.md`
- `WARDEN-WP-0014` (proxy), `WARDEN-WP-0017` (discoverability), `WARDEN-WP-0018` (lane activation)