railiance-platform/docs/whynot-design-npm-publish-handoff.md
codex 0ae7cdab92
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Update Gitea prose to Forgejo; place forge; record ArgoCD as an open decision
Gitea has been replaced by Forgejo. Updates prose mentions in docs/ while
deliberately preserving live names that a blind sweep would have broken:
GITEA_BACKEND_TOKEN is a provisioned OpenBao field in CCR-2026-0002, GITEA_URL
is a Makefile variable, and gitea-db is a running cnpg cluster. Capitalisation
discriminates prose from identifiers. Archived workplans are left as historical
record.

Blueprint 5.2: forge placement decided - workload to rapp-forgejo, layer
responsibility to S4 railiance-enablement, which already declares the handoff
contract and gains its first concrete owned responsibility.

Blueprint 5.6: whether Railiance should use ArgoCD at all is recorded as an open
decision rather than acted on by removing mentions. S3 runs four live ArgoCD
Applications plus AppProjects, and the two most recent commits here add more, so
deleting the documentation would describe a system that does not exist. The
question is real; it needs a decision and a migration, not a docs edit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:41:30 +02:00

253 lines
7.3 KiB
Markdown

# whynot-design npm Publish Token Handoff
This is the next-session handoff for `CCR-2026-0001` and the
`whynot-design-npm-publish` access lane.
## Current State
- CCR: `CCR-2026-0001`
- Decision: `e6381a56-6b04-4fd5-b2de-f3ef59cde888`
- Status: active; non-secret OpenBao apply and verification checks passed
- Front door: `ready`, `resolvable=true`
- Positive verification: passed 2026-06-28
- Negative verification: passed 2026-06-28
- Catalog id: `whynot-design-npm-publish`
- Tenant/org: `coulomb`
- Workload/project: `whynot-design`
- Bound IAM group: `whynot-design`
- Secret path: `platform/workloads/coulomb/whynot-design/npm-publish`
- Field: `NPM_AUTH_TOKEN`
- Token source: Forgejo package token for
`https://forgejo.coulomb.social/api/packages/coulomb/npm/`
The operator reported that the Forgejo token was generated and stored in OpenBao.
Using the temporary operator token only for non-secret infrastructure work, Codex
confirmed that the policy exists, the OIDC role exists with the whynot-design
binding and redirect URIs, the secret metadata has the expected catalog id, and
the `NPM_AUTH_TOKEN` field is present. No secret value was printed, recorded,
or copied into Git, State Hub, chat, or workplans.
On 2026-06-28, the attended positive OIDC login advanced from a missing
`groups` claim to a bound-claim mismatch. That means the role now requests the
`groups` scope correctly, but the authenticating identity is not a member of
`whynot-design`. The `whynot-design` LLDAP group was created and verified.
The intended publisher/verifier identity was later added, positive
verification passed, then `platform-root` was temporarily removed for negative
verification. The negative check passed with a groups bound-claim mismatch, and
`platform-root` was restored to `whynot-design`.
## Safety Rules
- Do not paste `NPM_AUTH_TOKEN` into Git, State Hub, chat, shell history, logs,
workplans, or screenshots.
- Do not run verification with shell tracing enabled.
- Record only non-secret evidence: path, field name, metadata keys, policy name,
role name, actor, timestamp, and pass/fail result.
- Mark ops-warden catalog entries ready only after positive and negative
verification are complete. For this lane, both checks have passed.
## OpenBao Secret Check
In the OpenBao UI, confirm the secret exists under the `platform` KV engine:
```text
workloads/coulomb/whynot-design/npm-publish
```
Expected field:
```text
NPM_AUTH_TOKEN
```
Expected custom metadata:
```text
catalog-id = whynot-design-npm-publish
```
Do not reveal the value during review.
## Policy
Create or update ACL policy:
```text
workload-kv-read-whynot-design-npm-publish
```
Policy body:
```hcl
path "platform/data/workloads/coulomb/whynot-design/npm-publish" {
capabilities = ["read"]
}
path "platform/metadata/workloads/coulomb/whynot-design/npm-publish" {
capabilities = ["read"]
}
```
Equivalent CLI command from an approved OpenBao operator context:
```bash
bao policy write workload-kv-read-whynot-design-npm-publish \
openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl
```
## OIDC Role
Create or update:
```text
auth/netkingdom/role/whynot-design-workload-kv-read
```
Role payload:
```json
{
"role_type": "oidc",
"allowed_redirect_uris": [
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback"
],
"oidc_scopes": [
"openid",
"profile",
"email",
"groups"
],
"user_claim": "sub",
"groups_claim": "groups",
"bound_claims": {
"groups": ["whynot-design"]
},
"policies": "workload-kv-read-whynot-design-npm-publish",
"ttl": "15m"
}
```
Equivalent CLI command from an approved OpenBao operator shell:
```bash
role_payload_file="$(mktemp)"
trap 'rm -f "$role_payload_file"' EXIT
cat >"$role_payload_file" <<'JSON'
{
"allowed_redirect_uris": [
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback"
],
"oidc_scopes": [
"openid",
"profile",
"email",
"groups"
],
"bound_claims": {
"groups": [
"whynot-design"
]
},
"groups_claim": "groups",
"policies": "workload-kv-read-whynot-design-npm-publish",
"role_type": "oidc",
"ttl": "15m",
"user_claim": "sub"
}
JSON
bao write auth/netkingdom/role/whynot-design-workload-kv-read @"$role_payload_file"
```
The OpenBao Browser CLI cannot run this shell block and may treat
`bound_claims={...}` as a string. When staying in the Web UI, open the API
Explorer and submit the role payload JSON above with:
```text
method: PUT
path: /v1/auth/netkingdom/role/whynot-design-workload-kv-read
```
If the API Explorer asks for a path without the API prefix, use
`auth/netkingdom/role/whynot-design-workload-kv-read`.
## Non-Secret Reads
These commands should succeed from an operator-capable identity and do not print
the token value:
```bash
bao kv metadata get platform/workloads/coulomb/whynot-design/npm-publish
bao policy read workload-kv-read-whynot-design-npm-publish
bao read auth/netkingdom/role/whynot-design-workload-kv-read
```
## Positive Verification
Positive verification proves the approved whynot-design identity can fetch the
field without exposing it in logs.
Before retrying, confirm the account used for OIDC login is a member of the
`whynot-design` LLDAP group. If OpenBao reports:
```text
claim "groups" does not match any associated bound claim values
```
then the groups claim is present, but the account is not in `whynot-design` or
KeyCape did not emit that membership in the fresh login.
The positive verification passed on 2026-06-28. During that run, the CLI printed
the short-lived OpenBao login token; it was revoked immediately by accessor.
Prefer `bao login -no-print` for future attended verification if the installed
CLI accepts that flag.
Use an attended shell, keep tracing disabled, and suppress command output:
```bash
set +x
bao login -no-print -method=oidc -path=netkingdom role=whynot-design-workload-kv-read
bao kv get -format=json platform/workloads/coulomb/whynot-design/npm-publish \
| jq -e '.data.data.NPM_AUTH_TOKEN | type == "string" and length > 0' \
>/dev/null
```
Record only that the check passed.
## Negative Verification
Negative verification proves a non-whynot identity cannot read the same field.
Use a non-whynot identity or role and confirm the read is denied. Do not print
or store any token value.
Record only the denial result and non-secret audit timestamp/request metadata.
The negative verification passed on 2026-06-28. `platform-root` was temporarily
removed from `whynot-design`; OpenBao rejected the OIDC login with a groups
bound-claim mismatch, so no OpenBao client token was issued and the secret was
not read. `platform-root` was then restored to `whynot-design`.
## Activation
Only after these are true:
- secret metadata confirmed;
- policy exists and is scoped to the corrected `coulomb/whynot-design` path;
- OIDC role exists and binds only `groups=["whynot-design"]` with approved
browser/local CLI callback URIs and `groups` OIDC scope;
- positive verification passed;
- negative verification passed;
`CCR-2026-0001` is now `active`, and ops-warden can mark
`whynot-design-npm-publish` `ready`/`resolvable=true`.
Current front door:
```text
readiness = ready
resolvable = true
```