I propose adding this section to ci/README.md, after "Cache":
## Pins
CI checks the SDK archives it downloads (macOS, FreeBSD, NetBSD, OpenBSD) against fixed hashes. Lint pins its direct Python dependencies by version.
Container images and GitHub Actions are referenced by tag, GitHub Actions by major version. Git sources are fetched by tag or branch. None of these are pinned by hash, because such pins:
- delay normal and security updates until someone bumps the pin;
- require recurring manual maintenance;
- do not cover what is fetched at run time, nor the runner and cache infrastructure, so they can create false confidence about reproducibility.
For the trade-offs, see [#35754](/bitcoin-bitcoin/35754/) and [#36516](/bitcoin-bitcoin/36516/).
In #35754, maflcko and willcl-ark both suggested such a note. The list is maflcko's draft, with the third bullet narrowed to what a hash leaves out. A note keeps the question from being reopened, as it was in #36512.
Is this text acceptable?
Background
The discussion. #35754 pinned SDK archives, Python dependencies, container images, git sources and GitHub Actions by hash. After "Tend towards NACK" from maflcko and doubts from willcl-ark, its author reduced it to the SDK hashes and uv.lock and closed it. The SDK hashes were merged in #35875. An earlier version of #36512 proposed pinning actions by hash. willcl-ark drafted a longer note.
Against pinning, mostly about images and SDKs:
- maflcko recalls no real issue in the last decade that pinning would have fixed (comment).
- Pins delay normal and security updates until someone bumps them, so a pin added for security can lower it. Bumps are manual: there is no
ci updatecommand, and review must check each version-to-hash mapping (maflcko: 1, 2, 3). - On secrets, pinned or not, GitHub, the runner provider or any mutable dependency can still print them: "The solution is not to provide any secrets" (maflcko, #36512).
- On actions, a hash covers neither the actions it calls nor the WarpBuild cache infrastructure (maflcko, review). He added: "No objection, just leaving a comment".
- On images, actions and packages, pins seem "to mostly be adding to the maintenance burden", with no clear goal (sedited, comment).
- On images, pins would attract dependabot-style bumps that someone has to review (maflcko, review).
- On images, floating ones catch breakage in common environments, and reproducible builds belong to Guix (willcl-ark, comment). maflcko answered that "nightly/bleeding-edge stuff should mostly sit outside this repo", since it "would likely mark every pull red", and that "having major version pins, where it makes sense, is useful".
willcl-ark would back pinning image tags if CI did release builds, signing, or used secret keys. CI does none of these. On actions he wrote: "For the same reason I don't have much opinion of github actions pinning; I think it's fine either way." WarpBuilds/cache does "add their own fixes every week or so", and "There seems little benefit beyond pinning to a major version to avoid breaking changes."
For pinning:
- On external inputs in general, pins minimize "the changes that can happen without our explicit knowledge/approval", and "Updating new hashes is trivial" (l0rinc: 1, 2). maflcko replied that it is trivial for the SDKs, whose version and hash change together.
- Whoever controls an action's repository can move its tag. This happened to
tj-actions/changed-filesin March 2025 (GHSA-mrrh-fwg8-r2c3). A full hash cannot be moved. - A compromise of
reviewdog/action-setup@v1"may have contributed to" it (Wiz Research). It reached the other reviewdog actions that use it, "regardless of version or pinning method" (GHSA-qmg3-hpqr-gqvc). A hash on the outer action would not have helped there, as maflcko's point on transitive actions suggests. Both incidents were in the ecosystem, not in this repository. - The nine action references in this CI come from seven repositories. All are JavaScript actions (
using: node24), which cannot call other actions. So here a hash does cover all the action code that runs. It does not cover what an action fetches at run time, nor the WarpBuild infrastructure. - GitHub recommends a lockfile (below), or else a full-length commit hash.
Exposure. The workflows reference no secrets. The token is read-only (pull requests from forks, a push run on master). The remaining risk is indirect: push runs on master save the ccache, depends and vcpkg caches that pull request runs restore.
Cost of bumps. In the last 12 months those seven repositories published about 50 releases, fewer in the majors used here. Grouped monthly, that is up to 12 pull requests a year, each with mappings to check. There is no Dependabot configuration today.
Lockfiles. GitHub's workflow dependency lockfile, .github/workflows/actions.lock, is generated by gh actions-lock. It pins every action, transitive ones included, and checks each source repository's identity at run time. Workflow files keep their tags, and Dependabot can update it. It answers three objections: transitive actions, mappings checked by hand, unreadable workflow files. An update still waits for a re-lock. It is in public preview. If the practice is revisited, it is the option to evaluate once it leaves preview.