# 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: applied; non-secret OpenBao apply checks passed 2026-06-28 - Front door: `applied-pending-verify`, `resolvable=false` - 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: 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. 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; no user membership was changed. Add only the intended publisher/verifier identity to that group before retrying positive verification. ## 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", "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. 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"]` with approved browser/local CLI callback URIs and `groups` OIDC scope; - 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 = applied-pending-verify resolvable = false ```