Back to skills
SKILL.md
Npm Trusted Publishing
ASecurityPublish npm packages via GitHub Actions with OIDC trusted publishing and provenance. Use when setting up automated npm publishes, debugging ENEEDAUTH/E404 errors, configuring provenance attestations, or setting up a new package for publication.
- 8 stars
- 0 votes
- 0 copies
- 2 views
- Added September 6, 2026
Security analysis
96/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 2 files and shows the line behind each finding
npx -y skills add v1truv1us/ai-eng-system --skill npm-trusted-publishing --agent claude-codeAre you the author of Npm Trusted Publishing?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/v1truv1us-npm-trusted-publishing-82a24186)---
name: npm-trusted-publishing
description: Publish npm packages via GitHub Actions with OIDC trusted publishing and provenance. Use when setting up automated npm publishes, debugging ENEEDAUTH/E404 errors, configuring provenance attestations, or setting up a new package for publication.
metadata:
category: user-invoked
version: 2.0.0
disable-model-invocation: true
---
Default output: return only the result, blockers, and required evidence. Omit preambles, process narration, repeated context, confidence scores, and follow-up offers. Use at most five bullets unless a required artifact or schema needs more.
# npm Trusted Publishing
Publish npm packages from GitHub Actions without long-lived tokens. Uses OIDC
for authentication and generates provenance attestations automatically.
## How It Works
1. GitHub Actions workflow runs on tag push
2. Workflow requests short-lived OIDC token from GitHub
3. npm exchanges OIDC token for publish token via registry
4. Package publishes with provenance attestation (supply-chain security)
5. No `NPM_TOKEN` secret needed — ever
## Prerequisites
### npmjs.org (one-time per package)
1. Go to https://www.npmjs.com/settings/
2. Packages → your package → Settings → Trusted publishing
3. Provider: GitHub Actions
4. Organization or user: matches GitHub username exactly (case-sensitive)
5. Repository: matches repo name exactly (case-sensitive)
6. Workflow filename: `publish.yml` (exact — not `publish.yaml`)
7. No environment name unless using GitHub deployment environments
### package.json
```json
{
"name": "@scope/package-name",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "https://github.com/user/repo"
},
"publishConfig": {
"access": "public"
}
}
```
Requirements:
- `repository.url` matches `https://github.com/<user>/<repo>` (no `.git` suffix)
- Package must be public (private packages cannot use provenance)
- Repository must be public (private repos cannot generate provenance)
## Complete Working Workflow
```yaml
name: Publish to npm
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
type: string
dry_run:
description: 'Dry run'
required: false
type: boolean
default: false
permissions:
contents: read
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Publish to npm
if: <!-- not a dry run -->
run: npm publish --access public --provenance
```
## Node Version Note
| Node | Bundled npm | Trusted publishing |
|------|-------------|-------------------|
| 18.x | 9.x | ❌ Not supported |
| 20.x | 10.x | ❌ Not supported |
| 22.x | 10.x | ❌ Use `npx npm@11 publish` |
| 24.x | 11.x | ✅ Works directly |
| 26.x | 11.x | ✅ Works directly |
**For Node 20/22:** Use `npx npm@11 publish` instead of `npm publish`.
## Multi-Package Workspaces
For monorepos with multiple publishable packages:
```yaml
- name: Publish core
working-directory: ./packages/core
run: npm publish --access public --provenance
- name: Publish toolkit
working-directory: ./packages/toolkit
run: npm publish --access public --provenance
```
## Dry Run
Test without publishing:
```yaml
- name: Dry run
run: npm publish --dry-run --access public --provenance
```
## Provenance Verification
After publish:
```bash
# Confirm version is live
npm view @scope/package version
# Confirm provenance attestation exists
npm view @scope/package dist.attestations.provenance
# View full provenance details
npm view @scope/package --json | jq '.dist.attestations'
```
On npmjs.com package page, look for the **"Provenance"** badge.
## Debugging Failed Publishes
### Check npm version
```bash
npm --version # Must be 11.x for trusted publishing
```
### Check OIDC env vars
```yaml
- run: |
echo "ACTIONS_ID_TOKEN_REQUEST_URL: ${ACTIONS_ID_TOKEN_REQUEST_URL:+set}"
echo "ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${ACTIONS_ID_TOKEN_REQUEST_TOKEN:+set}"
```
### Check trusted publisher config
- Org/user, repo, and workflow filename are all **case-sensitive**
- npm does NOT validate the config on save
- Must match exactly: `https://github.com/Org/Repo` vs `https://github.com/org/repo`
### Common errors
| Error | Cause | Fix |
|-------|-------|-----|
| `ENEEDAUTH` | No `registry-url` in setup-node | Add `registry-url: 'https://registry.npmjs.org'` |
| `404 Not Found` | npm 10.x with trusted publishing | Use Node 24+ or `npx npm@11 publish` |
| `403 Forbidden` | Trusted publisher mismatch | Check case-sensitive org/repo/workflow |
| `EPUBLISHCONFLICT` | Version already exists | Bump version or check if already published |
## Security Best Practices
- ✅ Use `permissions: id-token: write` (minimal)
- ✅ No `NPM_TOKEN` secret needed
- ✅ No `--provenance` flag needed (automatic on GitHub Actions)
- ❌ Never commit `.npmrc` with auth tokens
- ❌ Never use `secrets.NPM_TOKEN` in workflow
- ❌ Never use long-lived publish tokens
## Related
- `ci-cd-and-automation` — General CI/CD patterns
- `shipping-and-launch` — Pre-launch checklists
- `security-and-hardening` — Secrets management
Files in this skill
- SKILL.md
- evals/evals.json
Attribution
Comments
Loading comments…