Stage releases on npm from a workflow that cannot publish - #18
Merged
Merged
Conversation
release.yml finds a version npm does not have, waits for security-audit to pass on that commit, and runs npm stage publish from the publish environment, which admits only main. A staged package is not public: a maintainer approves each one with 2FA. Its npm trusted publisher must leave direct publishing unchecked, so a stolen workflow can stage a version but never make it public. PACKAGES.md documents the release and approval steps. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
.github/workflows/release.yml, which stages the packages on npm instead of publishing them, plus a Releasing section in PACKAGES.md.How it works
main; permissions:contents: read,checks: read): compares each package's version with npm and fails if the three versions differ. If any is missing, it waits forsecurity-auditto pass on that commit. Any successful run counts, because a superseded run may be cancelled.planfound something to stage): the only job withid-token: write, and the only one in thepublishenvironment (main only, admin bypass off). It packs withpnpm packages:pack, so the staged archives are the onescheck.ymlverifies. It carriesdist/provenance.jsonnaming the audited commit. It stages core first, records each result in the job summary, and exits non-zero if any package failed.maindoes nothing.Staged packages are not public. A maintainer approves each one with 2FA (
npm stage approve, or the Staged Packages tab on npmjs.com). Each package's trusted publisher on npm must name this repo,release.ymland thepublishenvironment, and must leave "can also publish directly withnpm publish" unchecked. That way a stolen workflow can stage a version but cannot make it public.Tested
pgstencil auth stripe, in that ordere79cc4d, fails ona2bc185(whose audit failed), passes when a cancelled run sits beside a successful one, and keeps waiting when no run exists yetnpm stage publish <tarball> --dry-runaccepts the archive path and refuses to overwrite the already-published 0.2.0.Not tested yet
The OIDC path can only run in Actions, so the first real release is the test. Every failure I can foresee (npm too old, OIDC rejected, stage refused) stops before anything is staged. Three things to check on that first release:
npm view <pkg>@<version> dist.attestations)Merging this changes nothing: all three packages are already on npm at 0.2.0, so the workflow finds nothing to stage.
🤖 Generated with Claude Code