CONCEPT Cited by 1 source
Tag protection¶
Tag protection is a Git-server-side invariant that, once a named tag is published, the tag cannot be deleted or re-pointed to a different commit. It is the Git-ref clause of publish-time immutability and the structural defence against the tag-hijack supply-chain attack class.
The tag-hijack attack it blocks¶
Without tag protection, a maintainer (or a compromised maintainer credential) can:
- Publish
v1.4.2pointing to commitabc123. - Consumers clone / pin / vendor the release at
v1.4.2 → abc123and record that hash. - Later, the maintainer force-pushes
v1.4.2to point todef456— a different commit, possibly with a backdoor. - New consumers cloning
v1.4.2getdef456. Old consumers who re-clone also getdef456. Only consumers who verified the commit SHA see a mismatch — most don't.
The attack works because Git tags are mutable refs by default —
git push --force --tags is a supported operation. Platforms that
host Git repos have historically followed the protocol default and
let tag-force-push through.
Tag protection structurally removes the force-push capability: the server's ref-update path rejects any update to a protected tag that would change its commit pointer or delete it.
Relationship to asset locking¶
On a release-oriented platform, tag protection is necessary but not sufficient for supply-chain integrity:
- Tag protection alone secures the Git-side name → commit pointer. An attacker who can modify release assets (the compiled binaries, tarballs, attached files) can still swap them even while the tag itself is frozen.
- Asset locking alone secures the binary artifacts but leaves
the tag name mutable — an attacker can re-point
v1.4.2to a different release object entirely. - Tag protection + asset locking together freeze the complete name-to-bytes binding.
GitHub immutable releases ship both clauses as one feature, plus a signed release attestation as the portable verification receipt. (Source: sources/2025-10-31-github-immutable-releases-ga)
Canonical instance: GitHub immutable releases (2025-10-28)¶
"Tags for new immutable releases are protected and can't be deleted or moved."
Scoping notes:
- Tags for new immutable releases only — tags created manually
(
git push --tags), or tags for non-immutable releases, are not automatically protected. Repos that want manual-tag protection rely on GitHub's separate branch/tag-protection-rule system. - Non-retroactive: disabling immutability on the repo/org setting doesn't unprotect tags for already-immutable releases.
Architectural shape¶
Tag protection is enforced on the ref-update control-plane path, not as a periodic reconciliation job. The Git server rejects the update at push time:
$ git push --force origin v1.4.2
! [remote rejected] v1.4.2 (tag is protected on immutable release)
error: failed to push some refs
The rejection has to happen synchronously in the push path, because once a force-push lands even briefly, any consumer fetching in that window gets the wrong commit and the compromise has already happened.
This makes tag protection a write-path invariant rather than a
policy-check; it is architecturally the same shape as CHECK
constraints in a database vs triggers — the former are part of the
write contract, the latter are observed after the fact.
Why Git's protocol permits force-pushing tags in the first place¶
Git tags were designed with two use cases in mind:
- Lightweight tags: local bookmarks, essentially throwaway.
- Annotated tags: release markers with author + message + signature.
Git's protocol doesn't enforce the second meaning — both tag types are mutable refs. The convention is that annotated tags on a published repo are immutable, but convention is not enforcement. Platforms (GitHub, GitLab, Gitea) layer tag-protection enforcement on top of the mutable-by-default protocol.
Seen in¶
- sources/2025-10-31-github-immutable-releases-ga — GitHub Releases GA, 2025-10-28 (the tag-protection clause of the immutable-releases feature).
Related¶
- concepts/publish-time-immutability — the parent policy; tag protection is its Git-ref clause.
- concepts/release-attestation — the portable cryptographic companion to the server-side tag lock.
- systems/git — the protocol whose default mutable-tag semantics is what tag protection reverses.
- systems/github-releases — the product that enforces tag protection on immutable releases.