# whynot-design real publish closeout This runbook is the SECRETS-WP-0003 closeout path for turning the `whynot-design-npm-publish` pilot from a dry-run/demo lane into a real production `@whynot/design` publish through `secrets-engine exec`. No raw npm token, OpenBao token, token accessor, `NPM_AUTH_TOKEN`, package-token value, or private credential material may be written to Git, State Hub, chat, prompts, workplans, or normal logs. ## Current non-secret pointers | Pointer | Value | | --- | --- | | Catalog id | `whynot-design-npm-publish` | | Canonical decision | `e6381a56-6b04-4fd5-b2de-f3ef59cde888` | | Org/repo | `coulomb/whynot-design` | | npm package | `@whynot/design` | | npm registry | `https://forgejo.coulomb.social/api/packages/coulomb/npm/` | | Catalog mount/path | `secret/coulomb/whynot-design/npm/publish` | | Catalog field | `npm_token` | | Injected env var | `NPM_AUTH_TOKEN` | The decision UUID above is resolved from State Hub in normal operation. A UUID-named local fixture exists only as an offline fallback for throwaway demos. ## Source-side preflight From this repo, with State Hub reachable: ```bash scripts/whynot-real-publish-preflight.sh ``` Expected source-side result: - `decision inspect e6381a56-6b04-4fd5-b2de-f3ef59cde888` shows `source: hub` and `APPROVED`; - `policy publication whynot-design-npm-publish` resolves to the repo-clamped `NPM_AUTH_TOKEN` path while netkingdom remains `maturity-build`; - `route whynot-design-npm-publish --json` returns a pointer only. If OpenBao is not reachable or the value is not provisioned, `ready=false` is acceptable and must be handled by the operator steps below. ## Operator live gates 1. Create or confirm a dedicated Gitea bot account such as `se-whynot-design`. Record only non-secret evidence: username, owning org/repo, package scope, creation timestamp, and who confirmed it. 2. Confirm the bot can publish `@whynot/design` to the coulomb registry and cannot publish outside the intended org/scope. Record only pass/fail and package names. 3. Mint the Gitea npm/package token for that bot and place it in a mode-0600 file outside any Git worktree. Do not print the value. 4. Apply/provision through secrets-engine using approved OpenBao authority: ```bash BAO_ADDR=https://bao.coulomb.social \ secrets-engine apply whynot-design-npm-publish --stage prod \ --bootstrap-token-file ~/.secrets-engine/bootstrap/prod-whynot-design.token BAO_ADDR=https://bao.coulomb.social \ secrets-engine provision whynot-design-npm-publish --stage prod \ --field npm_token \ --from-file ~/.secrets-engine/handoff/whynot-design-npm.token \ --bootstrap-token-file ~/.secrets-engine/bootstrap/prod-whynot-design.token ``` 5. Verify without disclosing the value: ```bash BAO_ADDR=https://bao.coulomb.social \ secrets-engine verify whynot-design-npm-publish \ --field npm_token --positive --negative \ --negative-token-file /secure/path/reviewed-unrelated.token \ --bootstrap-token-file ~/.secrets-engine/bootstrap/prod-whynot-design.token ``` The unrelated token must represent a reviewed identity outside the lane policy; it is not the bootstrap token and must be held in a separate mode-0600 file outside Git. 6. In the `whynot-design` repo, coordinate a real version bump. Run the publish through child-only injection: ```bash BAO_ADDR=https://bao.coulomb.social \ secrets-engine exec --catalog whynot-design-npm-publish -- npm publish ``` 7. Confirm the published version appears in the coulomb Gitea npm registry. Record package name, version, registry URL, timestamp, and result only. ## ops-warden handoff ops-warden should route npm publish credential requests here and return a pointer, not a value: ```bash secrets-engine route whynot-design-npm-publish --json ``` Safe pointer payload: ```json { "catalog_id": "whynot-design-npm-publish", "kind": "kv", "decision_ref": "e6381a56-6b04-4fd5-b2de-f3ef59cde888", "owner": "coulomb/whynot-design", "stage": "prod", "next_command": "secrets-engine exec --catalog whynot-design-npm-publish -- npm publish" } ``` ## Revocation and cleanup Track the OpenBao bootstrap token and the Gitea bot package token in [hardening-backlog.md](hardening-backlog.md). After publish verification, remove any handoff/source token files with `shred -u` and revoke or rotate the Gitea bot token according to the recorded lifecycle task.