Skip to content

SYSTEM Cited by 1 source

GitHub Releases

GitHub Releases is GitHub's tagged-artifact distribution surface on top of Git tags. A release is a composite of:

  • A Git tag stored in the repo (points to the commit the release was cut from).
  • Release metadata — name, body / release notes, draft / prerelease flags, latest-or-not, author.
  • N release assets — arbitrary binary blobs (tarballs, installers, platform binaries, .whl / .gem / .zip / .msi, SBOMs) stored in GitHub's blob storage, downloadable via stable URLs.

Releases are GitHub's primary distribution surface for:

  • Open-source projects shipping versioned binaries outside a package manager (e.g. the latest release tarball on github.com/org/repo/releases).
  • CI/CD workflows that build and attach artifacts to a release.
  • Enterprise internal releases, tool distribution, and SBOM publishing.
  • Homebrew / Scoop / Chocolatey formulae that point at GitHub release asset URLs.

Immutability feature (GA 2025-10-28)

Historically, a published release was fully mutable: the owner could add, modify, or delete assets; move the tag to a different commit; or delete the release entirely. This left the distribution surface open to a cluster of supply-chain attacks:

  • Asset swap: compromised maintainer replaces v1.4.2.tar.gz with a backdoored tarball; consumers who re-download get the backdoor.
  • Tag hijack: compromised maintainer force-pushes the tag to a different, malicious commit; see concepts/tag-protection.
  • Silent withdrawal: release deleted, consumers lose reproducibility.

On 2025-10-28 GitHub shipped immutable releases to GA. When enabled at the repo or organization level, every new release is published with three layered guarantees:

  1. Asset immutability — assets cannot be added, modified, or deleted after publication. Enforced at GitHub's release-API and UI write paths.
  2. Tag protection — the Git tag for the release cannot be deleted or moved to a different commit. See concepts/tag-protection.
  3. Release attestation — a signed Sigstore-bundle attestation is issued, verifiable with gh attestation verify or any Sigstore-compatible tooling. See concepts/release-attestation and patterns/sigstore-bundle-attestation.

(Source: sources/2025-10-31-github-immutable-releases-ga)

Scope + non-retroactive properties

  • Setting granularity: repo-level or org-level.
  • Scope: only new releases are locked; existing releases remain mutable unless explicitly republished.
  • Disable is non-retroactive: turning the setting off doesn't unlock already-immutable releases. This is definitional for publish-time immutability — if it could be retroactively unlocked, the integrity guarantee would be worthless.

What's locked vs what's not

Locked (immutable) Not locked
Release asset list (add/delete) Release notes / body text (pre-GA clarification needed)
Individual asset bytes Latest-tag pointer across releases
Tag → commit pointer New releases can still be published
Attestation bundle The org/repo immutability setting itself

The post does not disclose whether release metadata fields (title, body, labels) are part of the immutability envelope; that would need to be confirmed against the docs.

Architectural placement

Releases sit on top of — but are architecturally distinct from — the underlying Git repository:

Git repo (blob / tree / commit / tag objects in Git object store)
  │  release points to a commit via a Git tag
Release (metadata + asset references in GitHub's control plane)
  │  asset URLs dereference to blob storage
Release assets (opaque binaries in GitHub's blob-storage backend)
  │  signed by Sigstore
Attestation bundle (Sigstore-format, portable)

Immutability is enforced at the release control plane (write-path rejection of modifications) and exposed as a portable receipt at the data plane (the attestation bundle travels with downloads).

Consumption patterns

  • Direct URL download: https://github.com/<org>/<repo>/releases/download/<tag>/<asset> — cacheable, stable, CDN-friendly. Pre-immutability, the URL could point to different bytes over time; post-immutability, bytes for a published-immutable release are stable.
  • GitHub CLI: gh release download <tag>.
  • Package-manager formulae: Homebrew / Scoop / Chocolatey / apt repos that reference release-asset URLs.
  • CI verification: gh attestation verify <asset> --repo <org/repo> or cosign verify-blob-attestation.
  • npm: package versions have always been append-only ("you can't overwrite a published version"); similar publish-time-immutability semantics but scoped to a single tarball per version rather than a multi-asset release.
  • Docker registries: tags are mutable by default, OCI image digests are content-addressed (immutable); cosign + Sigstore provides the attestation half. GitHub Releases immutability roughly ports the cosign+tag-protection model to Git-tag-rooted releases.
  • Crates.io: published crate versions are immutable; no tag-protection analogue because the source-of-truth is the registry, not a Git tag.

Seen in

Last updated · 200 distilled / 1,178 read