Conversation
The publish job authenticates with the workflow's own OIDC identity and nothing else: no NPM_TOKEN or NODE_AUTH_TOKEN reaches any job or step, id-token: write and the protected release environment are the whole of its authentication, and setup-node no longer generates a user npmrc whose only purpose is carrying a token. The declaration gains publishPath.authentication, which the checker validates and compares against the workflow: the grant the method rests on must be on the publish job, the trusted publisher must name this workflow filename and environment, and a credential carrying registryAuth is a contradiction under a tokenless method. The documentation check now runs in both directions, so a rotation procedure for a credential nobody declares is a refusal too. scripts/publish-toolchain.mjs pins the npm CLI floor at merge time inside the required verify job, and refuses a release dry run declaring a different toolchain.
The preflight stops proving a token is set and starts proving the authentication the declaration names for the publish path is available here, still before the build and without contacting the registry. It also asserts the resolved npm against the declared floor, so an npm that cannot do the OIDC exchange stops the release before anything is packed. Presence only: one of those variables is itself a credential and its value is never read, compared or printed. Two new suites, one per subject. tokenless-publish grades what the committed workflow, declaration and allow-set say (AC-1, AC-4, AC-5, AC-8, AC-12); publish-preflight-oidc grades what the command path does when it runs (AC-3, AC-6, AC-7, AC-9). Every grader is run against a mutation that breaks the property it asserts. test/publish-preflight.test.ts is superseded by the second file, which carries every case of it that is still true. The existing credential-surface and npm-config-allow suites had fixtures written around the token; those are retargeted at credentials that still exist, so what each case grades is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spec:
pipeline/active/S0317-config-1/spec.md(umbrella), gate verdictverdict-spec-1.md.No long-lived npm credential can publish a forged
@cosyte/*version any more, because the releaseworkflow's own OIDC identity is what the registry accepts.
What changed
release.yml'spublishjob carries no registry credential.NPM_TOKENandNODE_AUTH_TOKENare gone from the publish step and from the configuration allow-check step,
id-token: writeandthe protected
releaseenvironment stay exactly where they were, andactions/setup-nodeis nolonger given
registry-url, so nothing generates a user npmrc whose only purpose is carrying atoken. The registry itself is pinned by
npm-config-allow.json'srequirerule instead..github/credential-surface.jsondeclares the authentication underpublishPath.authentication: the method, the grant it rests on, the runtime evidence a preflightcan observe, the npm CLI floor, and the four trusted-publisher fields npmjs.com asks for.
credential-surface.mjsvalidates that block, checks the grant against the workflow, refuses atrusted publisher registered against a different workflow filename or environment, refuses a
declared registry credential on a tokenless path, and now runs the documentation comparison in
both directions.
NPM_CLI_VERSION, 11.19.1) thata reader can determine from the workflow file, and
ci.yml'srelease-dry-rundeclares the sameone.
scripts/publish-toolchain.mjs(new) asserts both inside the requiredverifyjob.scripts/publish-preflight.mjsstops proving a token is set. It proves the declaredauthentication is available in this environment and that the resolved npm meets the declared floor,
still before the build and without contacting the registry. Presence only: one of those variables
is a credential and its value is never read, compared or printed.
RELEASING.mdcarries the registration procedure with the exact fields, the post-2026-09-03allowed-action default, and the correction that the npm CLI, not pnpm, carries trusted
publishing on this path.
What this does NOT do
Registering the npm Trusted Publisher and deleting the organization
NPM_TOKENsecret are founderactions on npmjs.com and in GitHub settings. Register the publisher before this lands, or no
publish can succeed until it is registered (npm accepts OIDC in addition to a token, so registering
first is safe). AC-11 is the operator's and is proved only by the first real publish.
Nothing here changes what any package contains; no package version moves, so there is no changeset.
Evidence
pnpm test:root840/840 green,credentials:check,install-hardening,readme-check,drift-check,publish-toolchain,changeset-guard,release:notes,format:check,check:no-emdash,typecheckandlintall green locally. Every grader added here is run against amutation that breaks the property it asserts.