# 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: approved - Front door: `template`, `resolvable=false` - Catalog id: `whynot-design-npm-publish` - Tenant/org: `coulomb` - Workload/project: `whynot-design` - Secret path: `platform/workloads/coulomb/whynot-design/npm-publish` - Field: `NPM_AUTH_TOKEN` - Token source: Gitea package token for `https://gitea.coulomb.social/api/packages/coulomb/npm/` The operator reported that the Gitea token was generated and stored in OpenBao. Codex could not verify the metadata from the current token-helper identity: metadata lookup, policy read, and auth-role read all returned `403 permission denied`. No secret value was read or printed. ## 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. - Do not mark the ops-warden catalog entry ready until positive and negative verification are complete. ## 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", "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' { "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. Use an attended shell, keep tracing disabled, and suppress command output: ```bash set +x bao login -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. ## 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"]`; - positive verification passed; - negative verification passed; then `CCR-2026-0001` can move toward `active`, and ops-warden can mark `whynot-design-npm-publish` `ready`/`resolvable=true`. Until then, keep the front door as: ```text readiness = template resolvable = false ```