Skip to content

Why there are no digests here

An earlier design of this code — the version living inside krites — carried a table of platforms with a SHA-256 constant for each. It worked. It was also the wrong shape, and replacing it is most of the reason this module exists.

What a digest in source can and cannot say

A digest pinned in Go source proves the bytes have not changed since somebody wrote the constant. That is genuinely useful, and it is all it proves.

It cannot say who published the bytes. It cannot be rotated when a version is republished for a legitimate reason. It cannot be revoked when a version turns out to be bad — the constant sits in every binary already built, and the only remedy is a new release of every consumer.

And it scales badly in a way that quietly erodes it: a table of four platforms across three versions is twelve constants, each of which somebody has to have verified. In practice one person verifies the first and copies the pattern.

What replaced it

go/artifacts fetches by name and version, checks a signature over a manifest that names the artefact it describes, consults a signed index of what is approved, and hands back a path.

Each of the three properties a source digest lacked is now present:

  • Origin — a signature identifies a publisher, verified against a key embedded in the consumer's binary and the key published over WKD, which must agree.
  • Rotation — republishing means signing a new manifest, not releasing every consumer.
  • Revocation — the signed index carries approval status, so a withdrawn version stops resolving for consumers that already exist.

The digest did not go away. It moved into the signed manifest, where it is covered by the signature and can change when it legitimately needs to.

What this module is left with

Names. Which archive a platform wants, and which file to take out of it.

That is why this module holds no URLs, no digests, and no idea what is approved — and why it cannot fetch anything at all on its own. New refuses to build a resolver without a fetcher rather than defaulting to one, because a default would mean choosing trust anchors on the caller's behalf, and a shared library is close to the worst thing to be casual about: it gets executed.

The one place a digest is still right

The container image build pins an archive by SHA-256 in its Dockerfile — see provision a container image.

That is not a contradiction. A digest in a build recipe is checked at build time, reviewed when it changes, and rebuilt from the recipe. A digest compiled into a distributed binary is one nobody can change afterwards. Same primitive, different lifetime, and lifetime is the whole argument.