6.5 KiB
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_TOKENinto 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:
workloads/coulomb/whynot-design/npm-publish
Expected field:
NPM_AUTH_TOKEN
Expected custom metadata:
catalog-id = whynot-design-npm-publish
Do not reveal the value during review.
Policy
Create or update ACL policy:
workload-kv-read-whynot-design-npm-publish
Policy body:
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:
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:
auth/netkingdom/role/whynot-design-workload-kv-read
Role payload:
{
"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:
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:
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:
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:
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:
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-designpath; - OIDC role exists and binds only
groups=["whynot-design"]with approved browser/local CLI callback URIs andgroupsOIDC 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:
readiness = applied-pending-verify
resolvable = false