A checksum is a claim about bytes

source archive, SHA256, hex; optional dict for one named file

A checksum names bytes. It does not name the software those bytes claim to be.

bytes to SHA256 to the recipe field

A checksum is a claim about bytes. It is silent about whether those bytes are the software.

each archived name has its own SHA256; mismatch refuses

A checksum is a claim about named bytes. Two archives are two claims. A miss stops the build.

What you will be able to do

  1. Read a checksums block, including the case where one entry is a dictionary, and say why it is one.

  2. Say what a verified checksum establishes and what it leaves open.

  3. Say which parameters break loudly on a version bump and which break quietly.

The easyconfig is Python. It allocates more lines to describing source locations than to any other aspect. The following excerpt presents that section in isolation.

name = 'code-server'
version = '4.130.0'

source_urls = ['https://github.com/coder/code-server/releases/download/v%(version)s/']
sources = ['code-server-%(version)s-linux-%(mapped_arch)s.tar.gz']
checksums = [
 {
 'code-server-4.130.0-linux-amd64.tar.gz':
 '3de23052e34fa705b3817efa66201cbc8d8ba6615b4cd03120c39bfc0ae1b7ab',
 'code-server-4.130.0-linux-arm64.tar.gz':
 '795366c40d0b32bc6fbbedf8dc992f9767a6125cc6dce965766c5f79e06bb725',
 }
]

Three parameters name the bytes: source_urls, sources, and checksums. The third is the checksum.

The list is positional, and then one entry is a dictionary

The variable sources is a list. The variable checksums is a list. Elements of checksums correspond to elements of sources by position. The first checksum belongs to the first source. The outer list in this example contains exactly one element. That element is a dictionary rather than a string. The dictionary maps filenames to checksums. The dictionary contains two entries for the single source. Both facts are required to interpret the structure. The dictionary exists. An easyconfig is Python. The single line can resolve to different filenames on x86-64 and on aarch64. One source line therefore yields two possible files. The dictionary indicates which checksum belongs to each file. The keys of the dictionary are full resolved filenames. The keys are not architecture identifiers such as amd64 or arm64. The dictionary does not contain architecture information. The dictionary stores names. The templating system determines which name to look up. Adding support for a third architecture does not cause the dictionary to raise an error for the missing entry. The lookup simply fails to find a key for that architecture on the machine that provides it. A per-architecture dictionary remains silent about architectures that are not listed.

What a checksum can and cannot report

A checksum verifies the bytes. Its sole purpose is to verify the bytes. It cannot indicate that the archive contains the expected version. If upstream re‑tags a release and the archive changes, the checksum fails. Suppose upstream publishes a different archive also labeled 4.130.0 and a checksum is computed for that archive. The checksum then passes indefinitely and reports nothing. It cannot indicate that the archive matches the layout expected by your easyconfig. This mirrors the failure in An easyconfig is Python: the name said 4.130.0, the archive was something else. The two claims merit side‑by‑side comparison.

The checksum verified against the archive.
The archive did not contain the directory the build expected.

Both statements hold simultaneously. Verification succeeded at full strength on exactly the right bytes. The build then failed on start_dir: the directory named in the file was not in the unpacked archive.

EB-Checksum-1 — One architecture, one missing key

A site adds riscv64 support to the code-server easyconfig. The build passes on every x86-64 and arm64 worker and fails on the riscv64 one, before anything is compiled.

The sources line is unchanged. The checksums block still carries the two entries from the page’s sample, keyed code-server-4.130.0-linux-amd64.tar.gz and code-server-4.130.0-linux-arm64.tar.gz.

  1. What is the failure, precisely?

  2. Why did no x86-64 or arm64 build catch it?

  3. What is the smallest change that fixes it, and what would you need in order to make that change?

Solution

The dictionary has no key for the filename that architecture produces. %(mapped_arch)s resolved to riscv64, which the block does not mention, so the lookup found nothing to verify against. (aarch64 would not have failed: that easyblock maps it to arm64, and that key is already there.)

No x86-64 or arm64 build could catch it: the template resolves to a key that is present. The dictionary is keyed by resolved filename rather than by architecture, and it says nothing at all about the filenames it omits. A per-architecture gap is invisible on every architecture except the one with the gap.

The fix is one more entry in the dictionary. To make it you need the actual checksum of the actual archive for that architecture, which means fetching it, which is the point: you cannot derive it from the others, and copying a value from anywhere else would verify the wrong file.

Which is why a source change is the dangerous kind

An easyconfig is a Python file that defines parameters describing the source shape. It also defines a rule for editing. Changing the version requires updating the checksum. The build stops on a mismatch and returns the computed value. Forgetting is not possible. The loudness is a property of the parameter, not of any diligence. Changing the kind of thing being downloaded does not produce loudness. The QMCPACK easyconfig, a Python easyconfig, built from a pinned development commit and switched to a release tarball. That change rewrites sources and updates the checksum, drawing attention to both. All other aspects of the source block also changed without announcement. A git checkout is archived under the repository’s name. The sources were one level down. A release tarball unpacks to name-version/ with sources at the top. start_dir described the initial arrangement and remained untouched. A checksum protects against wrong bytes. The format does not protect against correctly formed bytes arranged in a different shape.

Borrowing a checksum from another packaging system

Another situation occurs when a verified checksum is applied to the wrong archive. A single project version can correspond to several legitimate archives. GitHub generates a tarball from a tag. The project may also publish a release archive that often contains vendored dependencies or a generated configure absent from the tag. Both archives correspond to the same version. The archives differ in byte content. Neither archive is incorrect. A checksum copied from a conda-forge recipe or a Spack package.py can be entirely correct for an archive other than the one listed in source_urls. The checksum value is correct. The version is correct. The build fails a verification that operates exactly as intended. The following rule is narrow and worth preserving. A borrowed checksum requires the URL over which it was computed. The version alone is insufficient. Two ecosystems that agree on a version number do not necessarily agree on an artifact.

Loud and quiet

Version bumps are safer than source changes. The two break in different ways.

Change

How it is found

The version

The checksum mismatches and the build stops.

The archive’s layout

start_dir or a patch level still parses, and the build fails later on a path.

The kind of source, tag to release

Both of the above, and only the first announces itself.

A checksum borrowed from another ecosystem

Verification fails against an artifact that really is that version.

The first row contains everything a checksum protects. The other three rows are not the checksum’s responsibility. exts_list (the list of Python or R extensions the easyconfig installs after the parent) applies the same rows once per extension.

What to remember

  • A checksum names bytes, not a version and not an architecture.

  • A dict maps resolved filenames to checksums. The keys are names, not amd64.

  • A dict with two filename keys is two claims: each archive is checked against its own digest.