skip to content

What does the Go checksum database at sum.golang.org guarantee that go.sum alone cannot?

level: middleimportance: must knowfreq 55%

answer

  1. the first line has to come from somewhere
  2. trust on first use is the weakness
  3. an append-only public log, key in the toolchain
  4. checked before a new hash is written
  5. catches a moved tag for everyone

basics

~20 s

go.sum only pins the bytes your machine saw first, so a poisoned first download is pinned faithfully forever. The checksum database is a global append-only log of module hashes that the go command consults before writing a new go.sum line.

solid answer

~50 s

`go.sum` is trust on first use: it records the hash your machine observed the first time it fetched a module version, and thereafter refuses anything different. That is strong against later tampering and useless against tampering that happened before the line was written — a poisoned mirror or an intercepted first download is pinned faithfully. The checksum database named by `GOSUMDB`, `sum.golang.org` by default with its public key built into the toolchain, closes that gap: it is a global, append-only, tamper-evident log of module hashes, and before the go command writes a new `go.sum` line it looks the version up there and rejects the download if the hashes disagree. Because the log is append-only and proofs are checked against tree heads already seen, its operator cannot quietly serve one hash to you and another to everyone else. It also catches an author moving a published tag. What it does not do is judge the code.

code

text · 4 lines
text
$ go env GOPROXY GOSUMDB GOPRIVATE
https://proxy.internal.example/mod,direct
sum.golang.org
*.internal.example

go deeper

for a junior

Know that go.sum holds expected hashes and that a separate public database exists so the very first hash you record is checked against what everyone else sees, not just accepted.

for a middle

Explain trust on first use as the concrete weakness, name GOSUMDB and its default, and describe when in the workflow the lookup happens: before a new go.sum line is written, not on every build.

for a senior

Be ready to reason about what survives the check and what does not — a moved tag is caught, a malicious new release is not — and about keeping verification alive behind an internal mirror instead of disabling it.

for a principal

Own the argument for why the database stays on by default in your build platform, what compensating control replaces it wherever it is off, and how much operational cost mirroring the log is worth.

## The gap go.sum leaves open `go.sum` is a list of expected hashes, one or two lines per module version. When the go command downloads a module it hashes what arrived and compares it against that list; a mismatch aborts the build. This is a strong guarantee with one specific hole: **the first line has to come from somewhere.** If your very first fetch of `example.com/lib v1.4.0` was served by a compromised mirror, or intercepted, or came from an origin that had been taken over, then the hash of the hostile copy is what lands in `go.sum` — and from then on the file diligently guarantees that you keep getting the same hostile copy. That pattern is called trust on first use, and it is why a lockfile of hashes alone is not a supply-chain control. ## What the checksum database is The checksum database is a public, append-only, cryptographically verifiable log of `module version -> hash` records. The go command reaches it through the `GOSUMDB` setting, whose default is `sum.golang.org`; the log's public key ships inside the toolchain, so a candidate does not have to trust the network to authenticate the log's answers. Whenever the go command is about to add a hash for a module version that is not yet in `go.sum` — typically during `go get` or `go mod tidy` — it first asks the database what hash that version has. If the answer disagrees with what was downloaded, the download is rejected with a security error rather than being written into `go.sum`. The log is more than a lookup table, and the difference is the whole point. Its records are stored in a Merkle tree, and the go command verifies inclusion proofs for the records it fetches against signed tree heads, keeping the tree state it has already seen in the module cache so it can check that later views are consistent extensions of earlier ones. That structure means the operator cannot *retract* or *rewrite* a record, and cannot present a different view of history to one victim than to everybody else without the split becoming detectable. The property is tamper-evidence and global consistency, not secrecy. ## The three things it therefore buys you 1. **Your first fetch is no longer self-trusted.** A poisoned proxy would have to poison the global log too, which it cannot do silently. 2. **Re-tagging is caught.** Once `v1.4.0`'s hash is in the log it is there permanently. A maintainer who force-pushes that tag onto different commits produces bytes that disagree with the logged hash, and every team fetching that version afterwards is stopped — including teams who never had the original in their `go.sum`. 3. **Everyone's `go.sum` agrees.** Two independent teams that fetch the same version end up with the same hash, which is what makes a `go.sum` diff in a code review meaningful. ## The limits, which matter as much The database says nothing about whether code is *good*. A malicious author who publishes malicious code at a brand-new version gets that version recorded in the log like any other; verification passes and the attack succeeds. It is not an identity system: nothing binds a module path to a vetted publisher, and it does not sign on the author's behalf. It also only covers modules the go command actually looks up — set `GOSUMDB=off`, or match a module path with `GOPRIVATE` (or the narrower `GONOSUMDB`), and you are silently back to trust on first use for exactly those paths. ## Living without direct internet access A network with no egress does not have to give the check up. A module proxy is allowed to mirror the checksum database's endpoints as well as module archives, so an internal `GOPROXY` can answer the go command's log queries too, and verification keeps working end to end behind the mirror. That matters when standing up a self-hosted or air-gapped build environment: the tempting shortcut is `GOSUMDB=off` in the build image, and it quietly converts every dependency in the organisation back to whatever the mirror felt like serving on the day it was first ingested. If the mirror genuinely cannot proxy the log, the honest fallback is to do first ingestion of every new module in one controlled place where the database *is* reachable, commit the resulting `go.sum` lines, and treat that file as a reviewed artefact rather than a generated one. ## How to say it in an interview One sentence: `go.sum` protects you from a *change* to a dependency, the checksum database protects you from a *bad first impression* of one. Everything else follows from that.

  • What is the effect of setting GOSUMDB=off across an organisation's builds?
    `go.sum` still records and enforces hashes, so later tampering is still caught, but nothing checks the first hash you ever record against an outside source. Every module reverts to trust on first use, and whatever your proxy or mirror served on ingestion day becomes the permanent definition of that version for you. It is the single change that most quietly removes the guarantee while leaving every build green.
  • Can a build in a network with no internet egress still use the checksum database?
    Yes. A module proxy may mirror the checksum database's endpoints alongside module archives, so an internal `GOPROXY` can answer the log queries and verification works behind the mirror. Turning the database off is a choice, not a requirement of isolation. If the mirror genuinely cannot proxy the log, do first ingestion of each new module somewhere the database is reachable and treat the resulting go.sum lines as reviewed.
  • Does the checksum database tell you anything about whether a dependency is safe to use?
    No. It establishes that the bytes you got are the bytes everyone else got for that module version, and that the record cannot be rewritten later. A hostile package published at a fresh version is logged exactly like an honest one and verifies cleanly. Judging the code itself is a separate activity, and the database is deliberately not in that business.

go.sum is like writing down a stranger's signature the first time you meet and rejecting anyone who signs differently later; the checksum database is the public registry that tells you whether the first signature was theirs at all.

saying these in an interview costs you the question

  • Says go.sum and the checksum database are the same mechanism
  • Thinks the database certifies that code is safe or audited
  • Believes it verifies author signatures on releases
  • Claims verification requires trusting the network, not a built-in key
  • Assumes an isolated network must set GOSUMDB=off