Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 16 additions & 20 deletions .github/credential-surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,34 @@
},
"credentials": [
{
"name": "NPM_TOKEN",
"tokenClass": "npm Automation token, or a granular access token with publish rights on the @cosyte scope. A classic Publish token demands a 2FA one-time password CI cannot supply and dies with EOTP at the last step, after a green build.",
"storage": "organization",
"storageNote": "An organization secret on the Cosyte org, shared by every @cosyte/* repository. A repository-level NPM_TOKEN silently OVERRIDES the organization one, so it must live in exactly one place; rotating it affects every @cosyte/* repository, not just this one.",
"name": "GITHUB_OIDC_ID_TOKEN",
"tokenClass": "A GitHub Actions OIDC ID token, minted on request for a job that grants `id-token: write`, with the audience `npm:registry.npmjs.org`. npm 11.5.1 or later exchanges it, package by package, for a short-lived publish token, and the registry issues one only when the token's claims match that package's trusted publisher: repository cosyte/config, workflow release.yml, environment release. Nothing long-lived exists to issue, store or leak.",
"storage": "github-provided",
"storageNote": "Never stored. The runner sets ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN in every step of a job that grants `id-token: write`, and in no other job; the ID token itself is requested by npm inside `npm publish` and is never written to a file, a secret or the job's environment. What can drift is WHICH jobs hold the grant, and the GITHUB_TOKEN permissions below pin that per job. The trusted publisher that makes the exchange succeed lives on npm, per package, and is the owner's to register.",
"requiredForPublish": true,
"registryAuth": true,
"exposures": [
{
"job": "publish",
"step": "Publish",
"as": "env",
"name": "NPM_TOKEN",
"mode": "value"
"as": "runner",
"name": "ACTIONS_ID_TOKEN_REQUEST_URL",
"mode": "value",
"grantedBy": "id-token: write",
"note": "Set by the runner in every step of the publish job, not by the workflow. The Publish step is the consumer: changesets/action checks for both variables to choose trusted publishing, and npm, run by pnpm under `changeset publish`, requests the ID token with them."
},
{
"job": "publish",
"step": "Publish",
"as": "env",
"name": "NODE_AUTH_TOKEN",
"mode": "value"
},
{
"job": "publish",
"step": "The publish configuration must be one the allow-set permits",
"as": "env",
"name": "NODE_AUTH_TOKEN",
"as": "runner",
"name": "ACTIONS_ID_TOKEN_REQUEST_TOKEN",
"mode": "value",
"note": "S0081. The configuration allow-check runs earlier in this same gated job and must resolve the SAME configuration the publish resolves. The user npmrc actions/setup-node generates carries `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}`, and npm expands that reference while resolving; without the variable this step would read an unresolvable reference, which that gate is fail-closed about. NPM_TOKEN itself is deliberately NOT exposed here: nothing on the configuration path reads it, so copying it would widen this surface for nothing. The check prints every credential-denoting key with a fixed redaction marker instead of its value, and scrubs credential-shaped text from every byte it emits; test/npm-config-allow.test.ts asserts both, on a pass and on a refusal."
"grantedBy": "id-token: write"
}
],
"issuedForms": [{ "id": "npm-automation-token", "pattern": "npm_[A-Za-z0-9]{36}" }],
"retiredWhen": "An npm Trusted Publisher is registered for all eight @cosyte/* packages against org Cosyte, repository config, workflow release.yml, environment release. Removing this token before that publisher exists breaks every publish, so the order is not negotiable. RELEASING.md, 'Still deferred: OIDC trusted publishing', has the sequence."
"issuedForms": [],
"issuedFormsNote": "Empty, and explicit. The ID token is requested by npm and sent only as a request header, which npm's debug log does not record (npm 11.21.0's npm-registry-fetch logs the method, the status and the URL), and the token npm receives in exchange is held in npm's memory for one command and written to no file. The npm token shapes the log collector already redacts stay in release.yml regardless.",
"retiredWhen": "This repository stops publishing to npm from GitHub Actions, or npm retires trusted publishing. Removing `id-token: write` from the publish job retires nothing: it stops every publish, and the publish preflight refuses before the build when the two variables are missing."
},
{
"name": "RELEASE_PR_TOKEN",
Expand Down Expand Up @@ -129,7 +125,7 @@
"issuedForms": [
{ "id": "actions-installation-token", "pattern": "gh[pousr]_[A-Za-z0-9]{36,}" }
],
"retiredWhen": "Never, while this repository uses GitHub Actions. What retires is a GRANT: `id-token: write` on the publish job becomes the ONLY npm credential once trusted publishing is registered, and `pull-requests: write` on the version job retires if the Version Packages PR ever stops being opened by this workflow."
"retiredWhen": "Never, while this repository uses GitHub Actions. What retires is a GRANT: `pull-requests: write` on the version job retires if the Version Packages PR ever stops being opened by this workflow. `id-token: write` on the publish job does not retire: it is what GITHUB_OIDC_ID_TOKEN, the only npm credential, comes from."
}
],
"settings": [
Expand Down
Loading
Loading