How to checksum a release before you publish it
The build is done, the archives are in a folder, the release notes are half written. Checksums are the last job before upload and the order genuinely matters: signing, notarizing, stapling and re-zipping each rewrite bytes, so a digest taken too early describes a file nobody will ever download. Here is the sequence, what to publish, and the short block of copy that decides whether anyone uses it — with Rocket Hash doing the arithmetic.
Publishing a checksum is a small, cheap, permanent favor to everybody who downloads your work: packagers who need a value to pin, users behind a proxy that mangles large files, and the one person in six months whose download died at 94% and who would otherwise open a support ticket. It costs you about two minutes per release.
It is also easy to do in a way that helps nobody. A digest computed from the wrong copy of the artifact, published for a build you later replaced, or presented with no instruction at all is worse than nothing — it generates mismatch reports that are your fault, and it teaches people that checksums are noise.
So this page is about the mechanics of getting it right: where in your build the digests belong, what to put in the release, and what to write next to it. If you have not produced a manifest before, exporting a checksum manifest covers that end of it, and what a SHA256SUMS file contains is the page to link your own users to.
Every step that touches a file produces a new file as far as a hash function is concerned. The digest has to come after the last of them, and before the upload.
Where the digests belong in the build order
On macOS there are more byte-changing steps than people expect, and several of them happen after the point at which a build feels finished:
- Compile and assemble. The artifact exists but is not yet what you will ship.
- Code sign. Signing writes the signature into the binary or bundle. New bytes.
- Notarize. Uploading for notarization does not alter your copy, but stapling the resulting ticket into the disk image or app does. New bytes again.
- Package. Building the
.dmg, the installer or the archive makes a new container around everything above. - Hash. Now. Every file you are about to upload, in one pass, with one algorithm.
- Upload and publish — the artifacts and the checksums together, never one release apart.
The rule that follows from this is blunt: if you rebuild anything, throw the digests away and generate them again. Do not patch one line of the manifest by hand. And do not assume that rebuilding from an unchanged source tree reproduces an unchanged archive — most packaging tools record timestamps, so a second .zip of identical contents usually has a different digest; hashing a ZIP archive goes into why. That is not a fault, but it does mean yesterday's value cannot be reused.
Checksum a release
-
Finish every step that rewrites bytes
Signed, notarized, stapled, packaged, renamed to its final filename. If a step remains that touches the file — even compressing it once more, even a rename that your upload script performs — it happens before this point, not after. Hashing a DMG covers the Mac-specific wrinkle that the disk image and the app inside it are two different files with two different digests.
-
Put the final artifacts in one folder
One directory holding exactly what you will upload and nothing else — no
.DS_Storeyou care about, no earlier release candidate, no notes file. The folder becomes the definition of the release, which makes the manifest self-explanatory later. -
Hash them together with one algorithm
Drop the folder into the Files tool, set the Algorithm control to SHA-256, and let the run finish — each file is read once, and a handful of archives takes seconds. Then export, which writes the
shasum-compatible manifest that the rest of the world's tooling already knows how to read. -
Write the verification block
Four lines of prose in the release notes: which file holds the checksums, what it covers, what a match proves, and what it does not. Almost nobody writes this, and it is the difference between a manifest that gets used and one that gets scrolled past. The next section has wording you can lift.
-
Upload the manifest with the artifacts
The manifest belongs in the same release, attached as an asset next to the downloads it describes, so a user who finds the file finds the list. Publish it at the same moment as the artifacts — a checksum added an hour later is a checksum nobody trusts, because the gap is exactly when a swap would have happened.
-
Keep a copy of the digests yourself
Commit the manifest to the repository, or file it with the build log. Two releases from now, when somebody reports that version 2.4.1 behaves oddly, your own record answers in a minute whether they have your 2.4.1 or something else — and that is a question you cannot answer retroactively if the artifact has been replaced.
Which algorithm to publish
SHA-256, and only SHA-256 unless something downstream specifically demands otherwise. It is what SHA256SUMS implies, what packagers expect to pin, and what every platform's checksum tooling already handles — choosing a hash algorithm covers the handful of cases where the answer changes.
Publishing MD5 or SHA-1 alongside it is a downgrade rather than a courtesy. When several values are offered, a verifier uses whichever their tooling makes easiest, so a second line is effectively permission to check with a function that is no longer collision-resistant. And a publisher is precisely the party for whom collision resistance is the binding property: anyone who can influence what goes into your release gets to choose both of the colliding files, which is the cheap attack rather than the expensive one. MD5 collisions have been constructible since 2004 and take seconds; SHA-1's were demonstrated publicly in 2017. Neither has a practical preimage attack, and neither has the slightest difficulty noticing accidental damage — which is why MD5 still catches a truncated download perfectly well. But corruption is not what you are publishing against. The difference between those two properties is the whole argument, and how tamper-evidence actually works lays it out; whether MD5 is still safe covers where it remains reasonable.
One manifest, or one digest per file
| Form | Suits | Cost |
|---|---|---|
One SHA256SUMS asset in the release | Several artifacts — platforms, architectures, installers | A user who took one artifact out of five has to find the line that matches it |
A .sha256 sidecar per download | Downloads hosted on separate pages or mirrors | More files to keep in step; easy to forget one on a re-release |
| Digests pasted in the release notes | One or two artifacts, human readers | Not machine-readable; line wrapping mangles long hex |
A signed manifest, SHA256SUMS plus .asc | Anything security-sensitive, or widely mirrored | Key management, and a verification step to document |
For most projects the first row plus the last is the right answer: one manifest, signed, in the release. The notes then carry a pointer rather than 64-character strings, which saves you from the classic release-notes bug where a digest has been silently line-wrapped into two halves.
The sentence to write next to it
The manifest is the work; the four lines beside it decide whether anybody opens it. Name the file, say what it covers, say what a match proves, and say what it does not — in that order, in about sixty words, above the download links rather than under them.
Name it outright: SHA256SUMS lists a SHA-256 digest for every file in this release. That clause alone stops somebody checking the macOS installer against the source archive's line. Then what a match means, phrased so a non-specialist can act on it: if it matches, your copy is byte-for-byte identical to the one we built. Then the limit, immediately, in the next sentence — that is proof of a clean download and not proof of origin, and SHA256SUMS.asc is the file to check when origin is the question. Overstating this is what makes a careful reader doubt the rest of the page, and a reader who catches one overstatement starts hunting for the next.
Say what the file looks like as well, because most of the people downloading your work have never opened one. It is one line per artifact — a digest, two spaces, the filename that digest belongs to, and nothing else:
4f1b9c06a5d38e27bb0a7c4e5190df63a2c8741e0b6d5f92ac31e87d04b6fa15 myapp-2.4.1-macos.dmg
b82d5e47f9a0c1638d27be54a1f0c39d6e7b4821fa5d0c93e16b7a48df250e6c myapp-2.4.1-linux-x86_64.tar.gz
cd3760a81e5f2b94d0a86c17fb4e29d5830b7c641ae9f2d85b07c3e14fa62d98 myapp-2.4.1-windows-x64.zip
That is the format the export button writes, unchanged, which is most of its value: a user on any operating system can hand the list straight to whatever checksum tooling they already have, and one line of your notes can point them at how to read a SHA256SUMS file instead of you explaining two columns in a release announcement.
Check the filenames in the exported list against the filenames in the published release before either of them goes out. The second column comes from the folder you hashed, so a release script that renames an artifact on upload, or a host that appends a version to it, leaves you with lines that describe files nobody can find. A digest with the wrong name beside it is not a cosmetic problem: it is an easy way for an otherwise correct manifest to generate mismatch reports you then have to answer.
Signing the manifest
Whether a signature is worth anything to your users turns on how they get your key, not on the signature itself — sending a checksum so it means something sets out that reasoning. What follows is the publisher's half of it: the three choices that decide whether anybody can act on what you signed.
The form is a detached signature over the manifest — one extra file, conventionally SHA256SUMS.asc or SHA256SUMS.gpg, the pattern Linux distributions have used for decades. Publish the key's fingerprint somewhere that is not the download page and does not change between releases, because a key fetched from the page it vouches for proves nothing. Sign the manifest you are actually uploading, as the last step before the upload, or a rebuild separates the signature from the artifacts. And think before the signing key goes onto a machine that builds unattended: whatever can sign without you can sign whatever it is told to.
macOS does not ship GPG, so both you and your users will be installing something. If that is more ceremony than your project can carry, say plainly in the notes that the checksums guard against corrupt downloads and stop there — an accurate small claim beats an overstated large one. For Mac downloads specifically, code signing and notarization already make the origin claim a GPG signature would, and Gatekeeper checks them without your users doing anything at all.
Troubleshooting
A user reports a mismatch
Ask for three things before touching anything: the exact filename they downloaded, its size in bytes, and how many characters their computed value has. A wrong size means an incomplete download, a wrong character count means they ran a different algorithm, and a right size with a wrong digest on a file that verifies for everyone else usually means a proxy or a corporate filter rewrote it in transit. Then verify your own published artifact against your own recorded manifest — the possibility you are checking is that an upload went wrong at your end.
I rebuilt after generating the checksums
Regenerate the whole manifest and replace the published one. Editing a single line by hand works right up until the moment it does not, and a manifest where one line came from a different build than the rest is a support problem with no trail. If the artifacts are already public, say in the release notes that the checksums were updated and when.
The stapled build does not match what I tested
Expected, and not a bug. Stapling a notarization ticket writes into the artifact, so the file on your disk after stapling is not the file you hashed before it. The same applies to signing and to any repackaging. Hash last — and if your build pipeline is automated, put the hashing step immediately before the upload step so the order cannot drift.
Two of my artifacts have the same digest
Then they contain identical bytes, whatever their names say. Usually this is a universal build published twice under two architecture-specific names, or a release script that copied the same archive into two slots. Identical digests are a reliable signal, so treat it as a finding about your pipeline rather than a coincidence.
Nobody seems to use them
Most verification is done by machines, not people: package managers, build systems and mirrors pin a digest and check it on every install, which is a large part of why publishing one matters even when your own download page gets no questions. For the humans, the only lever you have is the copy — the manifest named outright, one line explaining what a match means, one naming the limit, and the file itself attached to the release rather than living in a wiki page from two years ago.
Frequently asked questions
Which checksum should I publish with a software release?
SHA-256, as a single SHA256SUMS file covering every artifact in the release. It is the value packagers expect to pin, every platform's tooling already handles it, and it remains collision-resistant. Publish one algorithm rather than several, and name the algorithm explicitly somewhere a human will read it.
Should I publish MD5 checksums as well as SHA-256?
Better not to. People verify with whichever value their tooling makes easiest, so offering MD5 invites checking with a function whose collisions have been constructible since 2004 — and a publisher is exactly the party who needs collision resistance, because an attacker who influences what you ship chooses both files. One clearly labeled SHA-256 is stronger than three options.
Where should the SHA256SUMS file go?
In the release itself, attached as an asset beside the downloads it describes, and published at the same moment as them. Put a pointer to it in the release notes rather than pasting the digests, since long hexadecimal strings get line-wrapped by almost every notes renderer. Keep a copy in your repository too.
Do I need new checksums if I re-upload the same build?
If the bytes are identical, the digest is identical and nothing needs to change. If you rebuilt — even from an unchanged source tree — assume the digest changed, because archive formats record timestamps and most builds are not byte-for-byte reproducible. Regenerate the whole manifest rather than editing one line.
Should I checksum the DMG or the app inside it?
The file people download, which is the disk image or the archive. The app inside it has its own digest that will not match, and a user who mounts the image and hashes the bundle will report a mismatch that is not one. If you also publish the inner value for some reason, label both unmistakably.
Does publishing checksums prove my release is not malware?
No. A digest proves a download is identical to what you built; it says nothing about what you built. Integrity and safety are separate claims, and conflating them in release notes is the kind of overstatement that makes careful readers distrust the rest. Code signing and notarization are the mechanisms that speak to origin.
Do I need to sign the checksum file?
If your download page and your checksum live in the same place, a signature is what turns “this arrived intact” into “this came from us” — because anyone able to replace the artifact can replace an unsigned digest beside it. A detached SHA256SUMS.asc is the standard form. If key management is more than your project can carry, publish the checksums anyway and describe them honestly as protection against corrupt downloads.