This document outlines the secure, tamper-resistant release protocol for npm packages. The setup uses GitHub Actions workflows to automate building, attesting provenance, and releasing packages while enforcing immutability through repository rulesets. This ensures that releases are verifiable, auditable, and protected against unauthorized modifications.
The protocol consists of two chained GitHub Actions workflows:
- Build & Push Workflow: Handles building the package and pushing built artifacts to a dedicated release branch.
- Attest & Release Workflow: Triggered only after a successful build; generates Sigstore attestations, creates GitHub releases, uploads assets.
Releases are published as immutable npm packages installable via npm install github:{owner}/{repo}#v{version}, with provenance attestations for verification.
- Chaining: Release workflows only run after successful builds, preventing arbitrary pushes from triggering releases.
- Provenance: Every release includes a Sigstore attestation proving the package's origin and integrity.
- Immutability: Release branches and tags are locked down via GitHub Rulesets, and releases are made immutable to block deletions, updates, or modifications.
- Automation: Dependency bots (e.g., Renovate, Dependabot) detect new versions automatically via tags.
- Transparency: All steps are logged in GitHub Actions, with attestations downloadable for verification.
Trigger: Push to main branch (or manual dispatch for testing).
Steps:
- Checkout the latest
main. - Set up Bun runtime.
- Install dependencies and run tests.
- Build the package (e.g., generate
dist/). - Extract version from
package.json(e.g.,1.2.3). - Create a new branch
release/v{version}containing only built artifacts (dist/,package.json, etc.). - Push the branch to trigger the downstream workflow.
Purpose: Isolates build logic and ensures only vetted, built code reaches release branches.
Trigger: workflow_run event on completion of the Build & Push workflow, filtered to release/v* branches, and only if the build succeeded.
Steps:
- Extract version from the triggering branch name (e.g.,
release/v1.2.3→1.2.3). - Checkout the
release/v{version}branch. - Set up Bun.
- Pack the package into a
.tgzfile usingbun pm pack. - Generate a Sigstore attestation for the
.tgzusingactions/attest-build-provenance. - Download the attestation as a JSON file (
sigstore-provenance-v{version}.json). - Create a Git tag
v{version}pointing to the branch's commit. - Create a GitHub Release for the tag, including release notes with installation instructions.
- Upload the
.tgzpackage and provenance JSON as release assets. - Push the tag to the remote.
Purpose: Handles attestation and release creation without exposing sensitive build steps.
- Sigstore Integration: Uses GitHub's built-in Sigstore attestation to cryptographically prove the package was built in CI and hasn't been tampered with.
- Offline Verification: The attestation JSON is uploaded as a release asset, allowing users to verify provenance even if GitHub's attestation view is unavailable.
- Subject: Attestation is tied to the packed
.tgzfile, ensuring the distributable artifact is attested.
- GitHub Immutable Releases: Once enabled, release assets (including the
.tgzpackage and provenance JSON) cannot be modified or deleted after publication, providing permanent tamper resistance. - Enabled on setup: Enabled by repository admin via Github CLI on initial setup phase.
- Rulesets: Manual creation of GitHub Rulesets via a local script (run with admin permissions):
Protect Release Branches: Blocks deletions, updates, force pushes, and enforces linear history onrefs/heads/release/v*.Protect Release Tags: Blocks deletions, updates, force pushes, and enforces linear history onrefs/tags/v*.
- No Bypass: Rules apply to all users, including admins, for maximum security.
- Immutable Releases: Enabled on initial setup via Github CLI/API to make release assets unmodifiable.
- Immutability: Release branches/tags and assets are permanently locked down.
- Chained Workflows: The attest/release workflow only runs after a successful build workflow, preventing malicious pushes to
release/v*from bypassing the build. - Branch Filtering: Only
release/v*branches trigger the downstream workflow. - Success Guard: Explicit check for
github.event.workflow_run.conclusion == 'success'.
- Required Permissions:
contents: write,attestations: write,id-token: write - Tools: Bun for packaging, GitHub CLI (
gh) for releases/attestations,jqfor JSON parsing. - No Exposed Secrets: Relies on
GITHUB_TOKENand OIDC; no hardcoded keys, nor tokens or PATs.
Once a release is published:
- Install via GitHub:
npm install github:{owner}/{repo}#v{version}. - Verify Provenance: Download the
sigstore-provenance-v{version}.jsonfrom the release assets and use Sigstore tools (e.g.,cosign verify-blob) to check the attestation. - Immutable Assets: Release assets are immutable, ensuring the package and provenance remain unaltered.
- Automation: Bots like Renovate will auto-detect new
v*tags and update dependencies.
- Updates: If package structure changes, update the build steps (e.g., what gets pushed to
release/v*). - Ruleset Management: Rulesets and immutable releases are set up on initial setup phase.
- Script Execution: Run
bash initial-setup.shlocally withghauthenticated as a repo admin to apply rulesets and enable immutable releases. - Logs: Review workflow runs for attestation details or API errors.
This protocol ensures releases are secure, verifiable, and bot-friendly. For questions or issues, refer to the workflow YAML files or GitHub Actions logs.