Skip to content

ci: publish to npm by trusted publishing instead of a stored token - #305

Merged
ralyodio merged 1 commit into
mainfrom
ci/trusted-publishing
Aug 5, 2026
Merged

ci: publish to npm by trusted publishing instead of a stored token#305
ralyodio merged 1 commit into
mainfrom
ci/trusted-publishing

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Switches publish.yml from a stored NPM_TOKEN to trusted publishing (OIDC). npm trades the short-lived token GitHub mints for this specific workflow run for permission to publish, so there is no long-lived credential in the repository to leak, rotate or forget — and none had to be minted, since the secret was never added.

Two things that would have failed quietly

Node 22 bundles npm 10, which predates trusted publishing and falls back to looking for a token that no longer exists — an auth failure that reads as a credential problem rather than a version one. npm is now upgraded first, and the 11.5.1 floor is checked rather than assumed.

The check compares with sort -V, because lexically 11.16.0 sorts below 11.5.1. Verified across the boundary:

npm verdict
10.9.0 reject
11.5.0 reject
11.5.1 accept
11.16.0 accept
12.0.0 accept

--provenance is removed — trusted publishing generates and attaches the attestation itself, so the flag is redundant.

Required before the next release

⚠️ Configure the trusted publisher on npmjs.com — it cannot be done from the CLI or API, only the web UI: the moshcode package → Settings → Trusted Publisher → GitHub Actions, with:

field value
Organization or user moshcoder
Repository moshcode
Workflow filename publish.yml
Environment (leave empty)
Allowed actions npm publish

It is pinned to the workflow filename, so renaming publish.yml breaks publishing with no other symptom. The header now says so.

Verification

  • YAML parses; 11 steps; permissions are exactly contents: read + id-token: write
  • no secrets. reference remains anywhere in the file
  • version gate exercised across the 11.5.1 boundary (table above)
  • the guards from ci: publish to npm when a release is published #304 are untouched: tests first, tag/version match, skip-if-already-published, post-publish registry confirmation

Note: npm does not allow an initial version to be published over OIDC, which does not apply here — moshcode@0.24.0 already exists.

🤖 Generated with Claude Code

Removes the NPM_TOKEN requirement. npm now trades the short-lived OIDC token
GitHub mints for this specific workflow run for permission to publish, so there
is no long-lived credential in the repository to leak, rotate or forget — and
none had to be minted, since the secret was never added.

Two things that would have failed quietly otherwise:

Node 22 bundles npm 10, which predates trusted publishing and falls back to
looking for a token that no longer exists — an auth failure that reads as a
credential problem rather than a version one. So npm is upgraded first, and the
11.5.1 floor is checked rather than assumed. The check compares with `sort -V`:
lexically, 11.16.0 is lower than 11.5.1.

`--provenance` is gone because trusted publishing generates and attaches the
attestation itself.

The other half of the trust lives on npmjs.com under the package's Trusted
Publisher settings, and it is pinned to this file's *name*. Renaming
publish.yml breaks publishing with no other symptom, which the header now says.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

92 finding(s)

HIGH/CRITICAL: 50 | MEDIUM: 42

Severity Rule Location
HIGH manifest-typosquat apps/pwa/package.json:19
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH secret-generic-credential apps/pwa/test/apikey-bearer-scheme.test.mjs:30
HIGH secret-generic-credential apps/pwa/test/apikey-mask.test.mjs:38
HIGH secret-generic-credential apps/pwa/test/apikey-reveal.test.mjs:35
HIGH secret-generic-credential apps/pwa/test/approvals-context.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/approvals-credits.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/approvals-notify.test.mjs:26
HIGH secret-generic-credential apps/pwa/test/approvals-resolve-race.test.mjs:20
HIGH secret-generic-credential apps/pwa/test/auth-form-email.test.mjs:29
HIGH secret-generic-credential apps/pwa/test/auth-form-email.test.mjs:33
HIGH secret-generic-credential apps/pwa/test/auth-page-error.test.mjs:36
HIGH secret-generic-credential apps/pwa/test/cli-device-token.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/cli-pages-balance.test.mjs:32
HIGH secret-generic-credential apps/pwa/test/cli-token.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/credits-pack.test.mjs:51
HIGH secret-generic-credential apps/pwa/test/credits-webhook-event-match.test.mjs:35
HIGH secret-generic-credential apps/pwa/test/credits-webhook.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/csrf-input-escaping.test.mjs:31
HIGH secret-generic-credential apps/pwa/test/csrf-input-escaping.test.mjs:101
HIGH secret-generic-credential apps/pwa/test/logout-csrf.test.mjs:29
HIGH secret-generic-credential apps/pwa/test/moshpit-api-key.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/moshpit-bulk-claim.test.mjs:23
HIGH secret-generic-credential apps/pwa/test/moshpit-claim-full-name.test.mjs:29
HIGH secret-generic-credential apps/pwa/test/moshpit-crawlable.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/moshpit-ending-page.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/moshpit-pins.test.mjs:22
HIGH secret-generic-credential apps/pwa/test/moshpit-pit-page.test.mjs:33
HIGH secret-generic-credential apps/pwa/test/moshpit-records-page.test.mjs:23
HIGH secret-generic-credential apps/pwa/test/moshpit-records.test.mjs:23
HIGH secret-generic-credential apps/pwa/test/moshpit-registry.test.mjs:20
HIGH secret-generic-credential apps/pwa/test/moshpit-related-endings.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/moshpit-sales.test.mjs:16
HIGH secret-generic-credential apps/pwa/test/moshpit-search.test.mjs:74
HIGH secret-generic-credential apps/pwa/test/moshpit-terms.test.mjs:19
HIGH secret-generic-credential apps/pwa/test/moshpit-tlds-pagination.test.mjs:28
HIGH secret-generic-credential apps/pwa/test/passkey-register-duplicate.test.mjs:38
HIGH secret-generic-credential apps/pwa/test/require-auth-next.test.mjs:31
HIGH secret-generic-credential apps/pwa/test/require-auth-next.test.mjs:35
HIGH secret-generic-credential apps/pwa/test/sessions-output-seq.test.mjs:30
HIGH secret-generic-credential apps/pwa/test/sessions-paste.test.mjs:24
HIGH secret-generic-credential apps/pwa/test/sessions-stream-replay.test.mjs:34
HIGH secret-generic-credential apps/pwa/test/sessions.test.mjs:24
HIGH secret-generic-credential apps/pwa/test/signature.test.mjs:6
HIGH secret-generic-credential test/auth.test.mjs:13
HIGH secret-generic-credential test/auth.test.mjs:63
HIGH secret-generic-credential test/console-cookie-malformed.test.mjs:15
HIGH secret-generic-credential test/console.test.mjs:12
HIGH secret-generic-credential test/mirror.test.mjs:37
HIGH secret-generic-credential test/mirror.test.mjs:77

…and 42 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 18d9084 into main Aug 5, 2026
4 checks passed
@ralyodio
ralyodio deleted the ci/trusted-publishing branch August 5, 2026 09:57
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.

1 participant