generated from coulomb/repo-seed
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:
@@ -67,6 +67,29 @@ entries:
|
|||||||
policy_ref: "flex-auth check secret.read:<domain>"
|
policy_ref: "flex-auth check secret.read:<domain>"
|
||||||
exec_capable: true
|
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
|
- id: flex-auth-policy-check
|
||||||
title: Authorization decision — may this actor perform this action
|
title: Authorization decision — may this actor perform this action
|
||||||
need_keywords: [authorization, policy, permission, allow, deny, may, flex-auth, topaz, pdp, decision]
|
need_keywords: [authorization, policy, permission, allow, deny, may, flex-auth, topaz, pdp, decision]
|
||||||
|
|||||||
@@ -553,6 +553,9 @@ def _entry_summary(entry) -> dict:
|
|||||||
else "route"
|
else "route"
|
||||||
),
|
),
|
||||||
"exec_capable": entry.exec_capable,
|
"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,
|
"wiki_ref": entry.wiki_ref,
|
||||||
"canon_ref": entry.canon_ref,
|
"canon_ref": entry.canon_ref,
|
||||||
"reviewed": entry.reviewed,
|
"reviewed": entry.reviewed,
|
||||||
|
|||||||
@@ -126,7 +126,15 @@ class Catalog:
|
|||||||
return [e for e in self.entries if e.is_active]
|
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]:
|
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]
|
tokens = [t for t in query.lower().replace("-", " ").split() if t]
|
||||||
pool = self.listed(include_draft=include_draft)
|
pool = self.listed(include_draft=include_draft)
|
||||||
scored = [(e.match_score(tokens), e) for e in pool]
|
scored = [(e.match_score(tokens), e) for e in pool]
|
||||||
|
|||||||
@@ -53,6 +53,21 @@ class RouteEntry:
|
|||||||
"""True when structured assist fields are present (advisory richness)."""
|
"""True when structured assist fields are present (advisory richness)."""
|
||||||
return any((self.auth_method, self.path_template, self.fetch_command))
|
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:
|
def match_score(self, tokens: List[str]) -> int:
|
||||||
"""Keyword-overlap score against need_keywords, title, and id.
|
"""Keyword-overlap score against need_keywords, title, and id.
|
||||||
|
|
||||||
|
|||||||
@@ -107,5 +107,5 @@ def test_access_ssh_lane_points_to_sign(monkeypatch):
|
|||||||
|
|
||||||
def test_access_no_match_exits_nonzero(monkeypatch):
|
def test_access_no_match_exits_nonzero(monkeypatch):
|
||||||
monkeypatch.setenv("WARDEN_ROUTING_CATALOG", str(_repo_catalog()))
|
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
|
assert r.exit_code == 1
|
||||||
|
|||||||
@@ -76,6 +76,29 @@ def test_real_catalog_has_one_executed_lane():
|
|||||||
assert [e.id for e in executed] == ["ssh-cert-host-access"]
|
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):
|
def test_no_double_source_rule_rejects_routed_steps(tmp_path):
|
||||||
bad = dict(ROUTED_ENTRY)
|
bad = dict(ROUTED_ENTRY)
|
||||||
bad["steps"] = ["do a thing on OpenBao"] # non-SSH entry must not carry steps
|
bad["steps"] = ["do a thing on OpenBao"] # non-SSH entry must not carry steps
|
||||||
|
|||||||
75
wiki/playbooks/whynot-design-npm-publish.md
Normal file
75
wiki/playbooks/whynot-design-npm-publish.md
Normal 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`
|
||||||
@@ -11,6 +11,7 @@ planning_priority: high
|
|||||||
planning_order: 17
|
planning_order: 17
|
||||||
created: "2026-06-27"
|
created: "2026-06-27"
|
||||||
updated: "2026-06-27"
|
updated: "2026-06-27"
|
||||||
|
state_hub_workstream_id: "cf8b392e-7624-4585-8935-a85e29202935"
|
||||||
---
|
---
|
||||||
|
|
||||||
# WARDEN-WP-0017 — Access front-door discoverability
|
# 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
|
id: WARDEN-WP-0017-T01
|
||||||
status: done
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "6e98df42-b5b4-49f8-a444-3c6346c8abd7"
|
||||||
```
|
```
|
||||||
|
|
||||||
- [x] `warden route` table: three-valued `warden` column — `issue` / `assist`
|
- [x] `warden route` table: three-valued `warden` column — `issue` / `assist`
|
||||||
@@ -73,6 +75,7 @@ priority: high
|
|||||||
id: WARDEN-WP-0017-T02
|
id: WARDEN-WP-0017-T02
|
||||||
status: done
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "6e2a7067-1afc-4f38-8d99-4d5c36a4661c"
|
||||||
```
|
```
|
||||||
|
|
||||||
- [x] `.claude/rules/credential-routing.md`: reframed the lead ("issues SSH certs **and**
|
- [x] `.claude/rules/credential-routing.md`: reframed the lead ("issues SSH certs **and**
|
||||||
@@ -88,6 +91,7 @@ priority: high
|
|||||||
id: WARDEN-WP-0017-T03
|
id: WARDEN-WP-0017-T03
|
||||||
status: done
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
|
state_hub_task_id: "7199625b-e78e-4495-8ca0-076100ae9f08"
|
||||||
```
|
```
|
||||||
|
|
||||||
- [x] Registered the State Hub capability "Operator access front door (caller-identity
|
- [x] Registered the State Hub capability "Operator access front door (caller-identity
|
||||||
|
|||||||
@@ -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`
|
||||||
Reference in New Issue
Block a user