From d9c2c7b386804f8b197b18e3f738eb5fcdb7bb99 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Thu, 20 Aug 2026 17:55:59 -0700 Subject: [PATCH] ci(publish): publish without provenance from the self-hosted pool --- .github/workflows/publish.yml | 9 ++++++--- CHANGELOG.md | 1 + docs/releasing.md | 11 ++++++----- package.json | 3 +-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9b51b08..5992144 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -100,8 +100,11 @@ jobs: EXPECTED_VERSION: ${{ needs.verify.outputs.version }} EXPECTED_SHA256: ${{ needs.verify.outputs.sha256 }} RELEASE_TAG: ${{ needs.verify.outputs.tag }} - # The first scoped publication may use the organization NPM_TOKEN. - # Once npm Trusted Publishing is configured for this workflow, OIDC is tokenless. + # npm provenance and npm Trusted Publishing both reject a self-hosted + # runner ("Unsupported GitHub Actions runner environment"), and this + # organization has no GitHub-hosted runners, so publication uses the + # organization NPM_TOKEN and ships no sigstore attestation. The release + # evidence is the verify job, the SHA-256 receipt, and the tag identity. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} GH_TOKEN: ${{ github.token }} steps: @@ -132,7 +135,7 @@ jobs: || node -e 'process.exit(Object.values(JSON.parse(process.argv[1])).includes(process.argv[2]) ? 0 : 1)' "$tags" "$version"; then echo "$name@$version already published; skipping" else - npm publish "$package" --access public --provenance + npm publish "$package" --access public fi - name: Create immutable GitHub release run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 25e135a..3c9c8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to Playproof are documented here. ### Continuous integration +- Releases publish from the self-hosted pool without npm provenance: the registry rejects a sigstore bundle built on a self-hosted runner. The tag-to-commit check, the full gate, and the SHA-256 receipt on the GitHub release are the integrity evidence. - Every workflow job runs on the organization's self-hosted Linux pool with a per-job `uv` virtual environment and a per-job pnpm install directory; the real-emulator gates (Libbet on PyBoy, Airstriker on stable-retro, Breakout on ALE, CartPole and FrozenLake on Gymnasium) all run there. ## 0.2.0 diff --git a/docs/releasing.md b/docs/releasing.md index 843e8d3..6ea984b 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -8,11 +8,8 @@ Playproof publishes one verified archive to npm and attaches the same archive an 2. Enable GitHub Actions and branch protection for `main`. 3. Ensure the `@tangle-network` npm organization permits public package publication. 4. For the first publication, make the organization automation token available as the repository secret `NPM_TOKEN`. -5. After `@tangle-network/playproof` exists on npm, configure npm Trusted Publishing for: - - organization: `tangle-network`; - - repository: `playproof`; - - workflow: `publish.yml`. -6. Remove the token from the repository when OIDC publication is proven. +5. Keep the token in place. npm provenance and npm Trusted Publishing both require a GitHub-hosted runner; they reject a self-hosted one with `Unsupported GitHub Actions runner environment`. This organization publishes from its own pool, so the package ships without a sigstore attestation. +6. Configure npm Trusted Publishing only when GitHub-hosted runners are available to this repository again. ## Release gates @@ -54,6 +51,10 @@ The equivalent free-ROM regression (`pnpm test:pyboy-libbet`) runs in CI on ever The workflow is idempotent: retrying an already-published version verifies the artifact identity and skips the npm mutation. +## Release integrity without provenance + +A release carries four pieces of evidence instead of a sigstore attestation: the tag resolves to the exact commit the verify job checked out, `package.json` version equals the tag, the complete gate passed on that tree, and the SHA-256 of the one archive that was built is recorded in `SHA256SUMS` on the GitHub release next to the archive itself. Compare the digest of the npm tarball with that receipt to confirm the registry holds the artifact this repository built. + ## Prohibited release paths - Do not publish from a developer laptop. diff --git a/package.json b/package.json index 1864354..39a429c 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,7 @@ "claude-code" ], "publishConfig": { - "access": "public", - "provenance": true + "access": "public" }, "engines": { "node": ">=20.19"