attestation

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

1post · 30d
Nov 40 posts
  1. Three gates before your first npm publish: 2FA, provenance, and staged releases I published a small plugin to npm for the first time last week. It is a DeepSeek Harness plugin called dsh-img2img-config — a composer image editor plus a provider settings panel. The code was done in a day; getting it onto the registry took three separate attempts, each blocked by a different gate. None of the errors said what the gate was for. They said things like EUSAGE, E403, and 404 Not Found — the last one while publishing a package that did not exist yet. This post is the write-up I wish I had found first. Gate 1 — 2FA is a browser flow, not a code The account had 2FA set up with a security key, not a TOTP app. npm publish does not ask for a six-digit code in that case, so the first attempt looked like this: npm error code EOTP npm error This operation requires a one-time password. npm error Open this URL in your browser to authenticate: npm error https://www.npmjs.com/auth/cli/*** The URL is the actual gate. Open it in a browser that is already logged in to npm, confirm with the security key, and the waiting CLI continues. Two things make this confusing: 1. The URL is masked in non-interactive output. Run npm publish from a TTY and the prompt is obvious. Run it from a script, a background job, or an agent, and you get *** and no way to complete the flow. If you are automating, you cannot be the thing that authorizes a new package by hand — that is what trusted publishing is for (Gate 2). 2. An expired session token fails with 404, not 401. After the token in ~/.npmrc had been revoked, npm whoami returned E401 Unauthorized, but npm publish returned: npm error 404 Not Found - PUT https://registry.npmjs.org/my-package - Not found For a brand-new package name, the registry does not tell you whether the name is taken or whether you are simply not authenticated. It returns 404 either way. If a publish fails with 404 on a name you know is free, check npm whoami before you start debugging the package. One more detail worth knowing early: npm is tightening bypass-2FA tokens — account changes first, direct publishing later. The deprecation notice points at gh.io. Gate 2 — provenance: true cannot run on your laptop The package had this in package.json: { "publishConfig": { "access": "public", "provenance": true } } Locally that fails immediately: npm error code EUSAGE npm error Automatic provenance generation not supported for provider: null null here means "I am not running inside a supported CI provider, so I have no OIDC identity to sign a build statement with". Provenance is a SLSA attestation: it says this tarball was built by this workflow, from this commit, in this repository. A laptop has no such identity to vouch for. So you have two options, and they are not interchangeable: - Publish locally with --no-provenance. The package goes up without an attestation. Fine for a first version if you just need the name and a working tarball. - Publish from CI with --provenance and permissions: id-token: write. This is the one that produces the "Built and signed on GitHub Actions" badge on the package page. The CI route is not much more work. A minimal workflow: name: publish on: workflow_dispatch: jobs: publish: runs-on: ubuntu-latest permissions: contents: read id-token: write # required for provenance / trusted publishing steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v7 with: node-version: 22 registry-url: https://registry.npmjs.org - run: npm install -g npm@latest # OIDC publishing needs a recent npm - run: npm publish --access public --provenance With that workflow present in the repository, configure the trusted publisher once: npm trust github my-package \ --file publish.yml \ --repo my-org/my-repo \ --allow-publish npm trust list my-package then shows what you granted: type: github file: publish.yml repository: my-org/my-repo permissions: publish, stage publish From that point on, a workflow_dispatch run publishes with a signed attestation and no long-lived token. That is the whole point: the gate moves from "a human proves presence with a security key" to "a workflow proves identity with OIDC". You can check the result from the registry: curl -s https://registry.npmjs.org/-/npm/v1/attestations/my-package@0.1.0 | jq '.attestations[].predicateType' # "https://github.com/npm/attestation/tree/main/specs/publish/v0.1" # "https://slsa.dev/provenance/v1" Gate 3 — stage-only tokens and the 0.0.0-stage placeholder The first API token I created was allowed to publish, but only to the staging area. For an existing package that is fine — you stage a version, review it, approve it. For a package that has never been published, npm refuses: npm error 403 Forbidden - PUT https://registry.npmjs.org/my-package npm error Cannot publish "my-package": this token can only publish to a staging area, npm error and "my-package" does not exist yet. Create it first with a direct-capable npm error token, then use `npm stage publish`. (E_STAGE_REQUIRED) The message is oddly specific and worth reading twice: the first publish of a name needs a direct publish capability. Staged publishing is a mechanism for subsequent versions. What you get when a staged path does run is a placeholder version: versions: ["0.0.0-stage", "0.1.0"] _dist-tags: { latest: "0.1.0" } 0.0.0-stage is npm's own placeholder for staged publishing — its description literally reads "Temporary package placeholder for staged publishing". Do not unpublish it; it is the name reservation that lets the staged flow exist. Make sure you set latest to your real version: npm dist-tag add my-package@0.1.0 latest Useful commands from this family: npm stage publish, npm stage list, npm stage view <id>, npm stage approve <id>, npm stage reject <id>. The part nobody warns you about: propagation is not instant The CI run printed: npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access npm notice publish Signed provenance statement with source and build information from GitHub Actions + my-package@0.1.0 Green checkmarks, + my-package@0.1.0, done — except the version was not on the registry yet. For a few minutes, GET /my-package/0.1.0 returned 404, latest still pointed at the previous version, and the tarball URL 404'd. npm says as much, one line earlier: npm notice Your package is being processed and may take a few minutes to become available. So if a "successful" publish does not show up immediately, wait and re-check the registry directly instead of re-running the publish: curl -s -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/my-package/0.1.0 Checklist I now run before publishing anything 1. npm whoami — is the token still alive? 2. Is this the first publish of the name? Then plan for a direct/interactive publish, not a staged one. 3. Does package.json carry a dsh.bundle / framework manifest that the host can actually load? (For DeepSeek Harness plugins, dsh.client alone is not installable — the most common rejection reason on the community list.) 4. Are there guessed upper bounds in peerDependencies? A >=1.0.0 <2.0.0 on a host package becomes a silent refusal on the next major release. See deepseek-ai/deepseek-harness#10776 for the failure mode: the module resolves, apply() never runs, and the host logs nothing. Pin "no upper bounds" with a test rather than a comment. 5. Prefer the trusted-publisher path from day one: workflow + OIDC + --provenance. No stored token, and the supply-chain evidence is a side effect. What actually shipped Three attempts, one evening: - 0.1.0 — published by hand, with browser authorization and no provenance. - 0.1.1 — published from GitHub Actions through the trusted publisher, with a SLSA provenance attestation and a fixed peer range. The gates are not arbitrary. 2FA asks "is a human here?"; provenance asks "which build produced this artifact, and can anyone check?"; staged publishing asks "is this name already owned, and by whom?". Each one is answered by a different mechanism, which is why the first publish feels like three unrelated failures instead of one publishing model. The package: <npmjs.com> The repository: <Dogwind221/img2img-studio>

    1 reaction

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

Scan pending.

Live mentions badge

Show how many posts mention your project in the last 30 days.

Live mentions · last 30 days
npm/attestation mentions in the last 30 days