Safeguard
Software Supply Chain•

How does publishing a Go module work, and where does provenance fit?

Go modules are published by tagging a version, and the checksum database guards consumers. See what it proves, what it misses, and where provenance fits.

Safeguard Security Team
4 min read

You publish a Go module by tagging a version in version control, not by uploading to a registry. The module mirror and checksum database then make that version immutable for consumers. That proves everyone gets the same bytes. It does not prove who built them or from what, so provenance is a separate step.

How do Go modules get published?

Go has no upload step. A module version is a tag in the repository, and the go command, a module proxy, or a mirror fetches it from there. The Go team's launch post says the contents of each module version are immutable, which opens the door to caching and authentication. Since Go 1.13 the go command uses the Go module mirror and checksum database by default.

So "publish" mostly means tagging a commit you trust. Tag from a protected branch, and treat tag creation rights as release rights.

What does the checksum database prove?

When a consumer first adds a module, its hash goes into go.sum. The checksum database is a public log of those hashes, so the first user to fetch a version cannot be quietly served different content from everyone else. If an author later moves a tag to new code, consumers who already recorded the old hash will see a mismatch instead of a silent change.

That protects consistency. It does not say the first version was good. A malicious module tagged on day one is consistent for everybody.

Where does provenance come in?

SLSA describes provenance as a statement of what entity built an artifact, by what process, from which inputs. Its first level only requires that provenance exists and is distributed. Signed provenance from a hosted build platform is level 2.

For a library consumed as source, the artifact is the tagged source tree, and the useful claim is that the tag points at a reviewed commit on a protected branch. For a Go binary you ship, build in CI and publish provenance for the binary digest. The go command can embed module information in a binary, which helps consumers see which module versions went in.

A constructed example

A maintainer of example.org/acme/retry has a personal access token on a laptop. A phishing email steals it. The attacker pushes a new tag with a small data-exfiltration function. Consumers upgrading see a normal version bump. Had the repository required reviewed merges to the protected branch and restricted who may create release tags, the stolen token alone would not have produced a release.

How to check or apply this

  1. Protect the default branch, require review, and restrict who can create version tags.
  2. Use two-factor authentication on every account with write access.
  3. Release from CI only, so a tag triggers a build you can audit.
  4. For binaries, publish signed provenance covering the binary digest and keep it next to the release.
  5. Run vulnerability scanning before tagging and note the result in the release.
  6. Ask consumers to keep go.sum committed and to leave the checksum database enabled.
  7. Keep module paths and ownership documented so a takeover is noticed.

Safeguard can scan project dependencies and inventory them in an SBOM, which covers the consuming side of this chain.

Quick check

Does the checksum database tell me a module is safe?

No. It shows that everyone sees the same content for a version. It cannot say whether that content is good.

Do I upload a Go module anywhere to publish it?

No. You tag a version in version control and proxies and mirrors fetch from there.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.