feat(WARDEN-WP-0018): activate whynot-design npm publish lane + resolvable flag

railiance-platform finished provisioning the whynot-design npm publish lane
(CCR-2026-0001, commit 8f617fc: active, readiness=ready, resolvable=true, positive
fetch + negative denial verified). First concrete warden access --fetch-resolvable
non-SSH lane — end-to-end proof of the WP-0014 conduit + WP-0017 discoverability.

T1 — catalog entry whynot-design-npm-publish (active, exec_capable) with the
owner-confirmed zero-placeholder handoff: path platform/workloads/coulomb/whynot-design/
npm-publish (the superseded whynot-design/whynot-design/... form is not used), field
NPM_AUTH_TOKEN, OIDC role whynot-design-workload-kv-read, policy + flex-auth ref. Added
wiki/playbooks/whynot-design-npm-publish.md.

T2 — RouteEntry.resolvable (active + exec_capable + no <…> placeholder), surfaced in
route/access --json; Catalog.find resolves an exact catalog-id first so
`warden access whynot-design-npm-publish` is deterministic. Tests added; fixed a
no-match test query that substring-collided (no ⊂ whynot). 213 pass, lint clean.

T3 — notified whynot-design (zero-placeholder command + resolvable gate + path
correction) and confirmed activation to railiance-platform. Sibling lanes stay draft
per their deferral.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 00:32:00 +02:00
parent 46b340f45f
commit e8bb469033
9 changed files with 252 additions and 2 deletions

View File

@@ -67,6 +67,29 @@ entries:
policy_ref: "flex-auth check secret.read:<domain>"
exec_capable: true
- id: whynot-design-npm-publish
title: whynot-design npm publish token (@whynot/design → coulomb Gitea registry)
need_keywords: [whynot-design, whynot, npm, publish, npm_auth_token, gitea, registry, coulomb, package]
owner_repo: railiance-platform
subsystem: OpenBao
warden_executes: false
wiki_ref: wiki/playbooks/whynot-design-npm-publish.md#worker-checklist
canon_ref: net-kingdom/docs/platform-identity-security-architecture.md
reviewed: "2026-06-29"
status: active
# Concrete, owner-confirmed lane — railiance-platform CCR-2026-0001 (commit 8f617fc):
# status=active, access_frontdoor.readiness=ready, resolvable=true; positive fetch
# passed and negative (non-whynot) login denied. Zero-placeholder fetch: an automated
# caller can `warden access whynot-design-npm-publish --exec -- npm publish` directly.
# The path was corrected to the `coulomb` tenant — the whynot-design/whynot-design/…
# form is superseded; do not reintroduce it.
auth_method: "bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read"
path_template: "platform/workloads/coulomb/whynot-design/npm-publish"
fetch_command: "bao kv get -field=NPM_AUTH_TOKEN platform/workloads/coulomb/whynot-design/npm-publish"
policy_ref: "flex-auth check secret.read:whynot-design"
exec_capable: true
lane: secret
- id: flex-auth-policy-check
title: Authorization decision — may this actor perform this action
need_keywords: [authorization, policy, permission, allow, deny, may, flex-auth, topaz, pdp, decision]

View File

@@ -553,6 +553,9 @@ def _entry_summary(entry) -> dict:
else "route"
),
"exec_capable": entry.exec_capable,
# 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,
"wiki_ref": entry.wiki_ref,
"canon_ref": entry.canon_ref,
"reviewed": entry.reviewed,

View File

@@ -126,7 +126,15 @@ class Catalog:
return [e for e in self.entries if e.is_active]
def find(self, query: str, include_draft: bool = False, limit: int = 5) -> List[RouteEntry]:
"""Rank entries by keyword overlap with the query. Highest first."""
"""Rank entries by keyword overlap with the query. Highest first.
An exact catalog-id match wins outright — this is what makes a stable keyed
command (`warden access whynot-design-npm-publish`) resolve deterministically
regardless of keyword collisions with other lanes.
"""
exact = self.get(query.strip())
if exact is not None and (include_draft or exact.is_active):
return [exact]
tokens = [t for t in query.lower().replace("-", " ").split() if t]
pool = self.listed(include_draft=include_draft)
scored = [(e.match_score(tokens), e) for e in pool]

View File

@@ -53,6 +53,21 @@ class RouteEntry:
"""True when structured assist fields are present (advisory richness)."""
return any((self.auth_method, self.path_template, self.fetch_command))
@property
def resolvable(self) -> bool:
"""True when `warden access --fetch` can run this lane with no further input.
A resolvable lane is active, exec_capable, and its fetch command (with the path
inlined) carries no unresolved ``<...>`` placeholder. Template lanes — like the
generic ``openbao-api-key`` or the ``<domain>``-parameterized login — are *not*
resolvable until an owner ships concrete names. Lets an automated caller know
whether ``--fetch`` will work *before* attempting it (whynot-design request).
"""
if not (self.is_active and self.exec_capable and self.fetch_command):
return False
blob = f"{self.fetch_command} {self.path_template or ''}"
return "<" not in blob and ">" not in blob
def match_score(self, tokens: List[str]) -> int:
"""Keyword-overlap score against need_keywords, title, and id.

View File

@@ -107,5 +107,5 @@ def test_access_ssh_lane_points_to_sign(monkeypatch):
def test_access_no_match_exits_nonzero(monkeypatch):
monkeypatch.setenv("WARDEN_ROUTING_CATALOG", str(_repo_catalog()))
r = runner.invoke(app, ["access", "zzzz-no-such-need-xyzzy"])
r = runner.invoke(app, ["access", "zzzz qqqq xyzzy"])
assert r.exit_code == 1

View File

@@ -76,6 +76,29 @@ def test_real_catalog_has_one_executed_lane():
assert [e.id for e in executed] == ["ssh-cert-host-access"]
def test_whynot_design_npm_lane_is_concrete_and_resolvable():
"""The provisioned npm publish lane has no placeholders and reports resolvable."""
catalog = load_catalog(_repo_catalog())
e = catalog.get("whynot-design-npm-publish")
assert e is not None and e.is_active and e.exec_capable
assert e.resolvable is True
assert "<" not in e.fetch_command and ">" not in e.fetch_command
assert "platform/workloads/coulomb/whynot-design/npm-publish" in e.fetch_command
def test_generic_and_template_lanes_not_resolvable():
catalog = load_catalog(_repo_catalog())
# generic openbao lane has <FIELD>/<path_template>; login lane has <domain>.
assert catalog.get("openbao-api-key").resolvable is False
assert catalog.get("key-cape-oidc-login").resolvable is False
def test_find_exact_id_wins_over_keyword_collision():
catalog = load_catalog(_repo_catalog())
# "npm" alone collides with openbao-api-key; the exact id must resolve uniquely.
assert catalog.find("whynot-design-npm-publish", limit=1)[0].id == "whynot-design-npm-publish"
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

@@ -0,0 +1,75 @@
# whynot-design npm publish token
Date: 2026-06-29
Catalog: `whynot-design-npm-publish` (status `active`, `resolvable: true`)
Owner: `railiance-platform` (OpenBao) · provisioning CCR-2026-0001 (commit 8f617fc)
The `NPM_AUTH_TOKEN` that publishes `@whynot/design` to the coulomb Gitea npm registry
(`https://gitea.coulomb.social/api/packages/coulomb/npm/`). ops-warden **does not hold
this token** — it is the access front door: `warden access` proxies the read from OpenBao
**as the caller** and never persists, caches, or logs the value.
---
## Owner-confirmed lane (no placeholders)
| Field | Value |
| --- | --- |
| OpenBao path | `platform/workloads/coulomb/whynot-design/npm-publish` |
| Field | `NPM_AUTH_TOKEN` |
| KV mount | `platform` |
| Read policy | `workload-kv-read-whynot-design-npm-publish` |
| OIDC login | `bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read` |
| Bound group | `whynot-design` |
| flex-auth ref | `secret.read:whynot-design` (if tenant policy requires pre-approval) |
| Runbook (owner) | `railiance-platform/docs/workload-kv-access-lanes.md` |
> The `platform/workloads/whynot-design/whynot-design/npm-publish` path from early in the
> provisioning thread is **superseded** — the live path is under the `coulomb` tenant.
---
## Worker checklist
1. **Authenticate as yourself** (you need your own identity; ops-warden adds none):
```bash
bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read
```
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:
```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
```
The value transits to you (or the child env) and never enters ops-warden's memory, disk,
or audit log (metadata-only audit).
3. **Readiness gate (for automated callers).** Before attempting `--fetch`, check the flag:
```bash
warden route show whynot-design-npm-publish --json | jq .resolvable # true
```
`resolvable: true` means the lane is concrete and `--fetch` will run; a template lane
reports `false`.
4. **Publish is outward-facing and immutable.** `npm publish` is irreversible and public.
Even once the token resolves, hold for an explicit operator "yes, publish" — do not
auto-run it from an agent.
---
## Scopes
This lane is the **publish** token only. A separate **read/install** token (for consumers
of `@whynot/design`) is a distinct need and would be its own catalog id
(`whynot-design-npm-read`) once railiance-platform provisions it — do not conflate them.
---
## See also
- `wiki/OperatorAccessAssist.md` — the `warden access` front door + guardrails
- `wiki/CredentialRouting.md` — routing model
- `railiance-platform/docs/workload-kv-access-lanes.md`,
`workplans/RAILIANCE-WP-0006-workload-kv-access-lanes.md`

View File

@@ -11,6 +11,7 @@ planning_priority: high
planning_order: 17
created: "2026-06-27"
updated: "2026-06-27"
state_hub_workstream_id: "cf8b392e-7624-4585-8935-a85e29202935"
---
# WARDEN-WP-0017 — Access front-door discoverability
@@ -55,6 +56,7 @@ tracked separately); any new fetch capability (the proxy already exists).
id: WARDEN-WP-0017-T01
status: done
priority: high
state_hub_task_id: "6e98df42-b5b4-49f8-a444-3c6346c8abd7"
```
- [x] `warden route` table: three-valued `warden` column — `issue` / `assist`
@@ -73,6 +75,7 @@ priority: high
id: WARDEN-WP-0017-T02
status: done
priority: high
state_hub_task_id: "6e2a7067-1afc-4f38-8d99-4d5c36a4661c"
```
- [x] `.claude/rules/credential-routing.md`: reframed the lead ("issues SSH certs **and**
@@ -88,6 +91,7 @@ priority: high
id: WARDEN-WP-0017-T03
status: done
priority: medium
state_hub_task_id: "7199625b-e78e-4495-8ca0-076100ae9f08"
```
- [x] Registered the State Hub capability "Operator access front door (caller-identity

View File

@@ -0,0 +1,99 @@
---
id: WARDEN-WP-0018
type: workplan
title: "Activate whynot-design npm publish lane + resolvable readiness flag"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 18
created: "2026-06-29"
updated: "2026-06-29"
---
# WARDEN-WP-0018 — whynot-design npm lane activation + `resolvable` flag
**Trigger:** railiance-platform completed provisioning the whynot-design npm publish lane
(CCR-2026-0001, commit 8f617fc): `status=active`, `access_frontdoor.readiness=ready`,
`resolvable=true`, positive fetch passed + negative (non-whynot) login denied. They asked
ops-warden to activate the dedicated catalog selector and notify whynot-design. This is the
first concrete `warden access --fetch`-resolvable non-SSH lane — the end-to-end proof of the
WP-0014 conduit + WP-0017 discoverability work.
**whynot-design's spec** (msg 2687dc31) drove the shape: zero-placeholder command keyed by a
stable id, owner-confirmed concrete path/field/role, a machine-readable readiness flag, and a
publish-vs-read scope split.
**Boundary unchanged:** ops-warden holds no token; the lane proxies the read as the caller.
---
## Tasks
### T1 — Concrete catalog entry + playbook
```task
id: WARDEN-WP-0018-T01
status: done
priority: high
```
- [x] Added `whynot-design-npm-publish` to `registry/routing/catalog.yaml` (`status: active`,
`exec_capable`, `lane: secret`) with the **owner-confirmed, zero-placeholder** handoff:
path `platform/workloads/coulomb/whynot-design/npm-publish` (the superseded
`whynot-design/whynot-design/…` form is **not** used), field `NPM_AUTH_TOKEN`, OIDC
`bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read`, policy
`workload-kv-read-whynot-design-npm-publish`, flex-auth `secret.read:whynot-design`.
- [x] `wiki/playbooks/whynot-design-npm-publish.md` — worker checklist, scopes, operator
go-ahead note (publish is immutable + outward-facing). Catalog `wiki_ref` points to it.
- [x] Passes the `_assert_no_secret_material` guard (templates/identifiers only, no value).
### T2 — `resolvable` readiness flag + stable-id resolution
```task
id: WARDEN-WP-0018-T02
status: done
priority: high
```
- [x] `RouteEntry.resolvable` — true when a lane is active, exec_capable, and its fetch
command/path carry **no** unresolved `<…>` placeholder. Surfaced in the route/access
`--json` (`_entry_summary`). Generic `openbao-api-key` and the `<domain>` login lane
report `false`; `whynot-design-npm-publish` reports `true`.
- [x] `Catalog.find` now resolves an **exact catalog-id** match first, so
`warden access whynot-design-npm-publish …` is deterministic regardless of keyword
collisions (whynot-design's "stable keyed command").
- [x] Tests: `tests/test_routing.py` (concrete+resolvable lane, template lanes not
resolvable, exact-id wins); fixed a `test_access` no-match query that incidentally
substring-collided (`no``whynot`). 213 pass, lint clean.
### T3 — Close the loop
```task
id: WARDEN-WP-0018-T03
status: done
priority: medium
```
- [x] Notified whynot-design (reply 744977ae) with the zero-placeholder command
`warden access whynot-design-npm-publish --exec -- npm publish`, the `resolvable` gate,
the coulomb-tenant path correction, and the operator-go-ahead reminder.
- [x] Confirmed activation to railiance-platform (reply f76d3a9e). Sibling lanes
(`issue-core-ingestion-api-key`, `openrouter-llm-connect`) stay `draft` per their
deferral, pending CCR-2026-0002/0003 provisioning.
---
## Acceptance
- `warden access whynot-design-npm-publish` resolves to a concrete, owner-confirmed,
zero-placeholder lane; `--json` reports `resolvable: true`.
- Template/generic lanes report `resolvable: false`; exact-id lookup is deterministic.
- No secret value in catalog, playbook, tests, or logs; ops-warden holds nothing.
## See also
- `WARDEN-WP-0014` (proxy lane), `WARDEN-WP-0017` (discoverability)
- railiance-platform CCR-2026-0001, `docs/workload-kv-access-lanes.md`