Teams are told to make builds "hermetic" and "reproducible" as if the two words meant the same thing, but they prove different facts about an artefact and protect against different attackers. A hermetic build controls what goes into a build: every input is declared and pinned, and nothing is fetched from the network while it runs. A reproducible build makes a claim about what comes out: the same declared inputs give a bit-for-bit identical artefact, so anyone can rebuild and compare. This guide compares the two guarantees, maps them to the SLSA Build track, sets out the failure modes and costs of each, and gives a decision path for which artefacts need which. It is written for release engineers, platform teams and supply chain security leads. Implementation detail lives in the Reproducible Builds Guide and the SLSA and Build Provenance Guide; this page is about choosing.
Key takeaways
- Hermetic is about inputs, reproducible is about outputs. Hermeticity guarantees that the build used only declared, immutable inputs; reproducibility guarantees that those inputs always yield the same bytes, which is what makes independent verification possible.
- Neither is required by SLSA v1.2. Build L3 requires isolated, ephemeral builds and unforgeable provenance. The current specification explicitly separates isolation from hermeticity, and both hermetic and reproducible builds sit in the "future directions" material that may or may not shape a future Build L4.
- Hermetic builds make provenance complete; reproducible builds make it checkable. A hermetic build lets the provenance list every dependency by digest. Only a reproducible build lets a second, independent builder corroborate that provenance by obtaining the same hash.
- Only reproducibility catches a compromised builder. If the build platform itself injects code, as SUNSPOT did in the SolarWinds Orion builds, a hermetic build on that platform still signs the tampered output. A rebuild elsewhere that produces a different hash exposes it.
- Hermetic usually comes first. Most nondeterminism and most dependency attacks come from undeclared or network-fetched inputs, so declaring and pinning inputs is the cheaper first step and a prerequisite for practical reproducibility.
Definitions and what each proves
Hermetic build: the retired SLSA v0.1 requirements defined a hermetic build as one where all transitive build steps, sources and dependencies were fully declared up front with immutable references, and the build service prevented network access while the build steps ran. Bazel describes the same idea from the build tool side: a hermetic build system returns the same output for the same source and product configuration because it isolates the build from changes to the host system, treating tools as inputs rather than relying on whatever is installed on the machine.
Reproducible build: the Reproducible Builds project defines a build as reproducible if, given the same source code, build environment and build instructions, any party can recreate bit-by-bit identical copies of all specified artefacts. Verification is a comparison of cryptographic hashes. SLSA v0.1 used a matching definition: re-running the build steps with identical input artefacts results in bit-for-bit identical output.
The difference is easiest to see as two questions a verifier can ask about a release:
- Hermetic answers: "Do I know every input that went into this artefact, and could anything undeclared have slipped in during the build?"
- Reproducible answers: "If I take those same inputs and build them myself, on infrastructure I trust, do I get the artefact that was published?"
A build can be hermetic without being reproducible: a sandboxed, offline build that embeds the current timestamp produces different bytes each time. A build can also be reproducible without being hermetic in the strict sense: a project can pin every download by digest and still fetch it during the build, and the output can be identical across rebuilds until a mirror disappears. In practice the strongest posture combines both, because hermeticity removes most of the variance that breaks reproducibility.
Side-by-side comparison
| Dimension | Hermetic build | Reproducible build |
|---|---|---|
| Core guarantee | Only declared, immutable inputs were used; no network during build steps | Identical inputs yield bit-for-bit identical outputs |
| Where the property lives | The build platform and build tool (sandbox, network policy, pinning) | The source tree, toolchain and build scripts (removing nondeterminism) |
| Who can verify it | The platform operator, or a reviewer reading complete provenance | Anyone who can rebuild, including independent third parties |
| Threats addressed | Dependency substitution mid-build, retagged or mutable references, poisoned downloads, host tool leakage | Tampering by the build platform or its operators, binaries swapped after build, provenance that lies |
| Threats not addressed | A compromised builder; malicious code in the declared source or dependencies | Malicious source or dependencies (they reproduce faithfully); a toolchain compromise shared by every rebuilder |
| Effect on SLSA provenance | Makes resolvedDependencies complete rather than best effort | Lets a second builder corroborate the provenance |
| Main cost | Mirroring and pinning every input, sandbox breakage, slower dependency updates | Hunting nondeterminism, toolchain pinning, running and monitoring rebuilders |
| Operational side benefits | Reliable caching, offline builds, survives registry outages | Debuggable diffs between builds, audit evidence, confidence in hotfix rebuilds |
How each maps to the SLSA Build track
SLSA v1.2 has Build levels L0 to L3. None of them requires a hermetic or reproducible build, and the build requirements page explicitly warns readers not to confuse its isolation requirement with hermeticity, which it describes as roughly meaning no network access. The table shows where each property sits.
| SLSA level | Relevant requirement | Hermetic adds | Reproducible adds |
|---|---|---|---|
| Build L1 | Provenance exists, completeness is best effort, no isolation required | A complete input list, but unauthenticated | Little: there is no trusted provenance to corroborate |
| Build L2 | Hosted build; provenance generated by the control plane and signed | Signed, complete dependency list | A way to check the signed claims independently |
| Build L3 | Signing secrets out of reach of user-defined steps; ephemeral environment per build; overlapping builds cannot influence each other; cache poisoning prevented, with identical output whether or not the cache is used | Closes the gap the specification leaves open: an isolated build can still fetch from the network | Detects the one thing L3 trusts by design: the build platform itself |
| Retired v0.1 "SLSA 4" | Hermetic required; reproducible "best effort", with a justification if not met | Required | Encouraged |
| Possible future Build L4 | The v1.2 future directions page says a Build L4 could enable corroboration of provenance, for example by providing complete knowledge of the build inputs | Likely relevant (complete inputs) | Likely relevant (corroboration) |
Two practical points follow. First, Build L3 is the realistic target for production software and is achievable without either property. Second, the L3 cache rule already contains a reproducibility-like test: SLSA requires that the output of a build be identical whether or not the cache is used, and its threats page says caches should be keyed by the transitive closure of all inputs, which is only meaningful if those inputs are declared. A team chasing L3 cache safety is already halfway to hermeticity.
Threat model for each
What hermeticity defends against
- Mid-build fetches: a build script that runs a package install or downloads a script at build time can receive whatever the network serves that minute. A poisoned release published to a registry minutes before a build, a retagged CI action or a compromised CDN all enter here. See the Dependency Pinning vs Version Ranges guide for the pinning side.
- Undeclared host inputs: Bazel lists host binaries in
/usr/bin, absolute paths and system compilers used for autoconfiguration as sources of non-hermeticity. These are also places where an attacker with access to a shared runner image can influence output without touching the repository. - Exfiltration from the build: a build with no network access during its steps cannot send pipeline secrets out, which limits the payoff from a malicious dependency's install script. This is a useful side effect rather than the main goal.
What reproducibility defends against
- A compromised build platform: SLSA L3 provenance is generated by the trusted control plane. If that control plane, a privileged administrator or malware on the build host alters the output, the provenance is signed correctly and describes the right source. In the SolarWinds supply chain compromise, SUNSPOT injected SUNBURST into Orion builds while the source repository stayed unchanged, and the tampered builds were signed and shipped as normal updates. An independent rebuild from the same source would have produced a different hash.
- Post-build substitution: the Go team frames the threat as attackers silently swapping distributed binaries while leaving the source untouched; anyone who can rebuild can confirm the posted binaries match without disassembling them.
- Dishonest or mistaken provenance: reproducibility turns a signed statement into a claim that others can test.
What neither defends against
Both properties faithfully build whatever the declared source says. The XZ Utils backdoor is the instructive case: the payload lived in release tarballs and a modified build-to-host.m4 macro, not in the git source most reviewers read. A hermetic build that declared the tarball as its input, and a reproducible rebuild from that same tarball, would both have reproduced the backdoor. What would have helped is building from the reviewed git revision rather than a maintainer-produced tarball, or reproducing the tarball itself from git. Source integrity is the job of the SLSA Source Track and code review.
Failure modes
| Failure | Property it breaks | How it shows up | Control |
|---|---|---|---|
| Network fetch inside a build step (install scripts, curl to a script, unpinned base image pull) | Hermetic | Build fails when egress is blocked; provenance lacks the fetched item | Fetch and pin inputs in a separate, recorded step; deny egress during build steps |
| Escape hatches in the sandbox (for example Nix fixed-output derivations, which keep network access by design, or a relaxed sandbox mode) | Hermetic, partially | Inputs are verified by hash after download, but download location and timing vary | Accept only hash-checked fetches; inventory every exception |
| Timestamps, build paths, locale, file ordering | Reproducible | Two clean builds of one commit give different hashes | SOURCE_DATE_EPOCH, path trimming, fixed locale and timezone, sorted inputs |
| Unpinned or host-supplied toolchain | Both | Rebuilds diverge after a runner image update | Treat compilers and SDKs as pinned inputs; build them hermetically too |
| Shared, writable cache | Both (and SLSA L3) | A poisoned cache entry is reused by later builds | Cache keyed by full input closure, writable only by the control plane |
| Rebuild on the same platform | Reproducible verification | Hashes match, but both builds ran on the compromised system | Rebuild on separately operated infrastructure with separate credentials |
| Rebuilds run but nobody reads the result | Reproducible verification | Mismatch dashboards turn red and stay red | Make a mismatch a release blocker or a security alert with an owner |
Cost and tooling by ecosystem
Effort depends heavily on the ecosystem. The examples below are taken from each project's own documentation and are illustrative, not exhaustive.
| Ecosystem or tool | Hermetic support | Reproducibility support | Typical effort |
|---|---|---|---|
| Bazel | Tools managed as inputs, content hashes for source identity, strict per-action sandboxing recommended; remote execution exposes hermeticity gaps | Follows from hermeticity plus removing nondeterministic actions (timestamps, build IDs) | High to adopt, low once adopted |
| Debian | Builds in clean, declared build environments | SOURCE_DATE_EPOCH convention; on 13 November 2026 the project's test infrastructure showed 96.0% of forky amd64 and 92.9% of unstable amd64 packages reproducible | Mature, distribution-wide |
| Go | Module checksums; pure Go builds need no host C toolchain | Go 1.21.0 was the first perfectly reproducible toolchain; CGO_ENABLED=0 with -trimpath gives reproducible builds for programs without cgo; gorebuild checks published toolchains | Low for pure Go; cgo needs a pinned C toolchain, for example in a container image |
| Maven | Lockable plugin and dependency versions; no version ranges | project.build.outputTimestamp; reproducible mode on by default from Maven 3.10.0 and 4.0.0-beta-5; artifact:check-buildplan and artifact:compare | Moderate: plugin upgrades, same JDK major version and newline style |
| Nix | Sandbox on by default on Linux, with builds in private network namespaces; fixed-output derivations are the exception | diff-hook support for inspecting differing build results | High learning curve, strong guarantees |
The Maven documentation makes a point that applies everywhere: a local rebuild does not prove third-party reproducibility, because the username, working directory and other environment details can leak into the output. Rebuilding from a completely different setup is the real test.
How provenance attestation fits
Provenance is the glue between the two properties. In an in-toto SLSA provenance statement, buildDefinition.resolvedDependencies lists the artefacts needed at build time with digests. A non-hermetic build can only fill this field on a best effort basis, because the build may have pulled something the platform never saw. A hermetic build can fill it completely, which turns provenance into an exact recipe.
An exact recipe is what a rebuilder needs. The rebuilder reads the provenance, fetches the same source revision and pinned inputs, builds on its own infrastructure, and compares digests. A match is corroborating evidence from an independent party; publishing that result as its own attestation, signed by the rebuilder's identity, gives consumers two signatures from two trust domains instead of one. The in-toto Attestations Guide and the Sigstore Guide cover the formats and signing; the identities that sign provenance and rebuild attestations are workload identities and should be governed like any other non-human identity, with pinned issuers and subjects at verification time.
Decision guidance
- Reach SLSA Build L3 first. Isolation, ephemeral environments and signing keys out of reach of build steps address more realistic attacks than either property, at lower cost.
- Go hermetic for anything with a large dependency graph. If a build pulls hundreds of packages, mid-build fetches and mutable references are the most likely entry point. Start by denying egress during build steps in audit mode, logging what breaks, and moving each fetch into a declared, pinned input step.
- Go reproducible for artefacts others must trust without trusting your builder. Toolchains, operating system images, firmware, security agents, widely distributed open source packages and anything signed with a high-value code signing key are good candidates. These are the artefacts where a compromised build platform would have the largest blast radius.
- Add independent rebuilding only where someone will act on a mismatch. A rebuilder is a control only if a mismatch blocks a release or pages an owner.
- Do not let either property substitute for source review. Build from reviewed version control revisions, not maintainer-produced archives, and protect branches under the Source track.
Small organisations: use a hosted CI platform that meets Build L3, pin dependencies by digest, block build-step egress where the platform allows it, and choose ecosystems with good defaults (pure Go, current Maven). Independent rebuilding can be as simple as a scheduled job in a separate cloud account for the release artefacts customers install.
Large organisations and software vendors: standardise on a hermetic build system for core products, run rebuilders on infrastructure with separate administrators and credentials, publish rebuild attestations, and set reproducibility targets per product line rather than all at once.
Worked scenario: an endpoint agent vendor
A company ships an endpoint agent and a command line tool to thousands of customers. The agent runs with high privilege on customer machines, and the vendor's customers are asking for SLSA evidence.
- Baseline: builds already run on a hosted CI platform with provenance generated in a reusable workflow, which meets Build L3. Provenance lists only the source revision; dependencies are resolved during the build from public registries.
- Hermetic step: the team moves dependency resolution into a separate step that pulls from an internal mirror by digest, records each digest, then runs the compile and package steps with egress denied. Three build scripts that downloaded helper tools break and are fixed by vendoring the tools. Provenance now lists every input.
- Reproducible step: two clean builds of the same commit differ. A diff shows an embedded build date and absolute paths in debug symbols. The team sets
SOURCE_DATE_EPOCHfrom the commit date and adds path trimming. Builds now match on the CI platform. - Independent verification: a rebuilder runs in a separate cloud account, administered by the security team with its own credentials, and rebuilds every release from the provenance. It signs a rebuild attestation when hashes match and opens a release-blocking incident when they do not.
- Scope decision: the command line tool, which is lower risk, stays hermetic but is not independently rebuilt. The agent, which runs privileged on customer endpoints, gets both.
The outcome is evidence that answers both customer questions: exactly what went in, and proof from a second party that what came out matches.
Detection and measurement
- Hermetic coverage: percentage of production build pipelines that run build steps with egress denied.
- Dependency completeness: percentage of release provenance documents whose
resolvedDependencieslist every input by digest, checked against the lock files. - Sandbox exceptions: count of approved exceptions (network-enabled steps, fixed-output fetches, host tool use), each with an owner and expiry.
- Reproducibility rate: percentage of release artefacts that rebuild bit-for-bit on independent infrastructure, tracked per product.
- Mismatch response time: time from a rebuild mismatch to triage decision; any unexplained mismatch on a release artefact is a security event.
- Blocked egress attempts: network connections attempted during build steps; a new destination appearing in a stable pipeline is worth investigating.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | SA-10 Developer Configuration Management; SA-15 Development Process, Standards, and Tools | Controlled, documented build processes and toolchains |
| NIST SP 800-53 Rev. 5 | SR-4 Provenance; SR-4(3) Validate as Genuine and Not Altered; SR-11 Component Authenticity | Complete provenance (hermetic) and independent validation (reproducible) |
| NIST SP 800-53 Rev. 5 | SI-7 Software, Firmware, and Information Integrity; SI-7(15) Code Authentication; CM-14 Signed Components | Integrity checks on artefacts and rebuild attestations before use |
| NIST CSF 2.0 | PR.PS-06; GV.SC-05 | Secure development practices; supply chain requirements placed on suppliers, such as asking for rebuild evidence |
| SLSA v1.2 | Build L3 isolation and provenance requirements | Baseline both properties build on |
| MITRE ATT&CK v20 | T1195.002 Compromise Software Supply Chain | The technique a compromised build platform enables; reproducibility is a detection control for it |
Practitioner checklist
- Confirm production pipelines meet SLSA Build L3 before investing in either property.
- Inventory every network fetch that happens inside build steps and move each into a declared, digest-pinned input step.
- Deny egress during build steps, first in audit mode, then enforced, with an exception register that has owners and expiry dates.
- Treat compilers, SDKs and base images as pinned inputs, not host software.
- Key build caches by the full input closure and allow only the control plane to write them.
- Build two clean copies of a release and diff them; fix timestamps, paths, locale and ordering until hashes match.
- Rebuild high-value artefacts on separately administered infrastructure and sign the result as its own attestation.
- Make an unexplained rebuild mismatch a release blocker with a named owner.
- Build releases from reviewed version control revisions, never from hand-made source archives.
Standards and references
- SLSA: Build Track Requirements (v1.2)
- SLSA: Future Directions (v1.2)
- SLSA: Threats and Mitigations (v1.2)
- SLSA: Requirements (v0.1, retired)
- Reproducible Builds: Definitions
- Bazel: Hermeticity
- Go: Perfectly Reproducible, Verified Go Toolchains
- Apache Maven: Configuring for Reproducible Builds
Related NHI Mgmt Group resources: Reproducible Builds Guide · SLSA and Build Provenance Guide · Self-hosted CI/CD Runners and Build Agents · CI/CD Pipeline Identity Security Guide · Code Signing Security Guide