Publish by OIDC trusted publishing instead of the expired npm token
The publish credential is dead, and nothing said so
pm-github last reached npm on 2026.8.18. main reads 2026.8.18, with no matching tag and nothing on the registry. Sixteen of the eighteen published fleet packages are in the same state, all stopping on 2026-08-16 or 2026-08-17.
The release job fails at Publish npm package:
npm error code E404
npm error 404 Not Found - PUT https://registry.npmjs.org/pm-github - Not found
A 404 on PUT is a rejected write credential, not a missing package. The registry answers 404 rather than 401 so it does not disclose package existence to an unauthorised caller — which is why this reads like a normal first publish rather than an outage. The repository secret was already rotated on 2026-08-22 and the failures continued unchanged; npm whoami against the maintainer host's stored auth returns 401. The credential is dead, not mis-stored.
Nothing else noticed, because the version bump and the release commit both land before the publish step — deliberately, so an interrupted release resumes the same version. So main advances whether or not the artifact ships and every other job stays green.
The change
Trusted publishing removes the credential that can expire: the registry mints a short-lived one from the workflow's own OIDC identity. Two changes had to go together, and the second is the easy one to miss:
NODE_AUTH_TOKEN/secrets.NPM_TOKENare gone from the publish step.id-token: writeandregistry-urlstay — they are what make OIDC reachable.- npm is raised to
>=11.5.1before the publish step. node 22 ships npm 10.x, which has no trusted-publishing support and would silently fall back to token auth — producing exactly the same E404 and looking like the migration simply failed.
Verification
Two guards fail closed, and both were mutation-checked rather than assumed:
| Mutation | Result |
|---|---|
Reintroduce NODE_AUTH_TOKEN | OIDC test fails |
| Delete the npm upgrade step | npm-version test fails |
| Unmodified | both pass |
Gates: build, check, test, docstring, changelog:full + changelog:check, and pm health --strict-exit against the pinned binary — all green.
Requires a one-time npmjs.com setup binding pm-github to unbraind/pm-github and release.yml before the next release can publish. Until that exists this workflow fails closed, which is correct but is not a release.
pm item
Summary by Sourcery
Migrate release publication to fail-closed npm OIDC trusted publishing and protect the release workflow from stale credentials, unsafe execution paths, and unverified registry access.
New Features:
- Switch npm package publication to OIDC trusted publishing without long-lived npm tokens.
- Preflight npm and registry acceptance before release mutations so rejected publishing identities fail closed.
Bug Fixes:
- Prevent releases from advancing version and commit metadata when npm publishing credentials or trusted-publisher configuration are invalid.
- Prevent non-main workflow runs from executing release operations with publishing permissions.
Enhancements:
- Pin and verify a trusted-publishing-capable npm version at installation and publication time.
- Remove legacy npm credentials from user and global configuration before publishing.
- Harden release workflow handling of registry outages, rate limits, credential cleanup, permissions, and workflow-controlled inputs.
Deployment:
- Require a one-time npmjs.com trusted-publisher binding for the package, repository, and release workflow.
Tests:
- Add mutation-focused tests covering OIDC authentication, npm version enforcement, credential removal, workflow permissions, preflight ordering, and fail-closed behavior.
Chores:
- Ignore runtime PM transaction journals from version control.
- Add PM issue and history records for the release publishing migration.
Summary by cubic
Switches npm publishing from the expired stored NPM_TOKEN to OIDC trusted publishing, and makes the release fail before any version bump when npm rejects the workflow's identity.
- Gates the release job on
mainat the job level, keeping a step-level check as defense in depth. - Pins npm to
11.19.0, fails closed on the effective version at install, re-verifies it immediately before publication, and rejectsGITHUB_PATHwrites in between. - Preflights the OIDC id-token exchange before any mutation, encoding scoped names per npm's
escapedName, accepting any 2xx, and distinguishing a registry outage or rate limit from a misconfigured trusted publisher. - Scrubs every legacy credential from setup-node, a stray
.npmrc, and npm's global config, rejecting anynpm setornpm config setof a credential key. - Stores the exchange's short-lived publish credential in an
mktempfile removed by exactly one EXIT trap; the success message notes the exchange proves identity, not what the binding permits. - Passes repository metadata to run scripts through
env, so repository-controlled text can't be spliced into privileged shell commands. - Covers the workflow with an enumerated mutation suite of fail-closed cases, including the inline-permissions branch that used to report correctly declared permissions as missing.
- Fixes six guards that could fail open: the audit's catch rethrows everything except a missing control, unspaced
&&,||, and;split changelog commands, the release-job boundary accepts a leading underscore in job ids, both YAML block-scalar indicator orders are matched, and the changelog gate asserts the flag changes the heading instead of pinning today's date. - Adds an identity audit that fails any post-baseline commit with an unapproved author or committer or a new absolute home path.
- Ignores
.agents/pm/transactions/journals, which are runtime state that would trippm healthif committed.
Migration
Bind pm-github to unbraind/pm-github and release.yml on npmjs.com once before the next release; until then publishing fails closed.