Skip to content

ci(release): stage npm packages for 2FA approval instead of publishing - #29

Closed
hassiebp wants to merge 1 commit into
mainfrom
hassiebbot/npm-staged-publishing-2a2c
Closed

hassiebp wants to merge 1 commit into
mainfrom
hassiebbot/npm-staged-publishing-2a2c

Conversation

@hassiebp

Copy link
Copy Markdown

Problem

The CLI's release and beta workflows publish to npm as soon as they run. The npm-publish and npm-publish-beta environments currently have no required reviewers, and main is unprotected. So any account with write access, or a stolen GitHub token for one, can ship @langfuse/cli / langfuse-cli. The LiteLLM release compromise on Oct 21, 2026 shows that even reviewer gates on GitHub fall to stolen CLI OAuth tokens.

Changes

  • release.yml and beta-release.yml now run npm stage publish instead of npm publish. Staged versions are not installable until an npm maintainer approves them with npm 2FA. GitHub credentials cannot do that step.
  • The run summary lists each stage id: canonical package first, then the compatibility package.
  • Removed the "verify publish reached npm" polling steps. A staged version is not visible in the registry, and the short-lived OIDC token cannot run npm stage list.
  • The minimum npm version check moves from 11.5.1 to 11.15.0, the first version with npm stage. Node 24 currently ships npm 11.19.1.
  • MAINTENANCE.md documents the new approval step, the recovery path, and the trusted-publisher setup with stage publish only. scripts/release.ts help text updated.

Recovery behavior to review. Staged versions are invisible to release-guard.ts and beta-release.ts, so:

  • If the second stable stage fails, approve the staged @langfuse/cli first and then rerun. The guard sees it as published from the same gitHead and stages only langfuse-cli. A rerun before that approval fails on the already-staged version.
  • A second beta dispatch before the first is approved or rejected computes the same version and fails at the staging step.

Required npm-side setup before merging: both trusted publishers (release.yml / npm-publish on both packages, and beta-release.yml / npm-publish-beta on @langfuse/cli) must allow stage publish. Afterwards, remove direct publish from them. Separately, the two environments still need required reviewers and branch/tag policies, which MAINTENANCE.md already says are mandatory.

Verification

  • bun run typecheck: passes
  • bun test: 102 pass, 0 fail
  • zizmor --offline .github/workflows/: no findings (3 already suppressed, unchanged)
  • Built the CLI, then created both package folders with scripts/npm-packages.ts. Real npm@11.19.1 stage publish /tmp/clipk/canonical --ignore-scripts --tag latest --dry-run accepts the folder and flags; it stopped only on the expected "cannot publish over 1.2.4". Staging, like publishing, turns on provenance automatically under trusted publishing (lib/utils/oidc.js).
  • Not run: an end-to-end staged release. It needs the npm trust relationship above; a beta dispatch is the cheapest real test once that is configured.
Open in Web Open in Cursor 

Stable and beta release workflows now run npm stage publish. Staged versions are not installable until an npm maintainer approves them with 2FA, so GitHub credentials alone can no longer ship a release.

Co-authored-by: Hassieb Pakzad <hassiebp@users.noreply.github.com>
@hassiebp hassiebp closed this Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants