From e463544deee7836c2871cc5fe3ac8b508d20a02d Mon Sep 17 00:00:00 2001 From: Ondrej Machek Date: Fri, 24 Jul 2026 22:41:04 +0200 Subject: [PATCH] Publish via OIDC trusted publishing, with no token in the workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package is configured on npmjs.com with a trusted publisher pointing at this repo and publish.yml, and its publishing access is set to disallow tokens — so the OIDC exchange is now the only route to npm. Drop NODE_AUTH_TOKEN from the publish step. setup-node's registry-url writes `_authToken=${NODE_AUTH_TOKEN}` into .npmrc, and with no NPM_TOKEN secret that expands to an empty credential; npm can read that as auth-already- configured and skip the OIDC exchange, failing with a 401 after all five gates have already run. npm's own trusted-publishing example omits it. Record the constraints this puts on the release path in AGENTS.md — notably that the trusted publisher matches on workflow *filename*, so renaming this file silently breaks publishing, and that `release` events run the workflow from the tagged commit, so workflow changes must land before the tag. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/publish.yml | 22 +++++++++++++++------- AGENTS.md | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dfe00e1..2e370b4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,10 +3,13 @@ name: Publish # Publishes to npm with provenance attestations. Trigger by publishing a # GitHub Release (tag `vX.Y.Z`), or manually via workflow_dispatch. # -# One-time setup: add an npm Automation token as the `NPM_TOKEN` repository -# secret (Settings → Secrets and variables → Actions), OR configure a trusted -# publisher on npmjs.com (OIDC, no token needed). Provenance requires a public -# repo and the `id-token: write` permission below. +# Auth is a trusted publisher (OIDC) — npmjs.com → simple-builder → Settings → +# Trusted Publisher, pointing at this repo and this workflow filename. There is +# no token anywhere: npm exchanges the Actions OIDC token for a short-lived +# credential, which is why `id-token: write` is required below. The package is +# set to "disallow tokens", so this is the only publish route. Renaming this +# file, or moving the publish step to another workflow, breaks publishing until +# the trusted publisher is updated to match. on: release: @@ -35,7 +38,8 @@ jobs: # The tag is passed through `env:`, never interpolated into the script # body: `${{ }}` expands before bash runs, so a tag name containing shell # metacharacters (git permits $ ` " ; |) would otherwise execute here — - # in a job holding id-token: write and NPM_TOKEN. + # in a job holding id-token: write, i.e. able to mint a publish + # credential for the package. env: TAG: ${{ github.event.release.tag_name }} run: | @@ -68,9 +72,13 @@ jobs: node --unhandled-rejections=strict smoke.mjs - name: Publish with provenance + # No NODE_AUTH_TOKEN on purpose. setup-node's registry-url writes an + # .npmrc containing `_authToken=${NODE_AUTH_TOKEN}`; with no token + # secret that expands to an EMPTY credential, which npm can read as + # "auth already configured" and skip the OIDC exchange — a 401 after + # every gate above has run. Trusted publishing wants the token absent. + # npm >= 11.5.1 is what implements the exchange; 22.x ships older. run: | npm install -g npm@latest npm --version npm publish --provenance --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md index 573e469..7028b7e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -149,3 +149,19 @@ Kept OUT of the differential fuzzer's domain (covered by unit tests instead): Bump `version` in `package.json`, then publish a GitHub Release tagged `vX.Y.Z`. The publish workflow re-runs the full gate and publishes to npm with provenance. See `.github/workflows/publish.yml`. + +Auth is an npm **trusted publisher** (OIDC): npmjs.com has the repo and the +workflow *filename* on record, and npm exchanges the Actions OIDC token for a +short-lived credential at publish time. Consequences worth knowing before you +touch the release path: + +- There is no npm token, in repo secrets or anywhere else, and the package is + set to *disallow* tokens — this workflow is the only way to publish. Do not + add `NODE_AUTH_TOKEN` back: `setup-node`'s `registry-url` writes + `_authToken=${NODE_AUTH_TOKEN}` into `.npmrc`, and an empty value there can + read as configured-auth and suppress the OIDC exchange. +- Renaming `publish.yml`, or moving the publish step into a different workflow + file, breaks publishing until the trusted publisher is updated to match. +- The tag must agree with `package.json` — a guard step fails the run otherwise. + Workflows for a `release` event run from the *tagged* commit, so land any + workflow change on `master` before you cut the tag.