What Are Attestable Builds?
This document explains what attestable builds are, why they matter, and how they solve the software verification problem without requiring reproducible builds. After reading it, you'll understand the core insight behind attestable builds and why Trusted Execution Environments make them possible now.
We assume familiarity with git, package managers, and the concept of cryptographic hashes. No prior knowledge of confidential computing is required.
The Verification Problem
One party wants assurances about another party's software. The specific assurances vary by context:
- A customer deploying your service wants to know it's running the code you claim, built from the source they audited.
- A compliance team wants evidence that a binary was built with specific dependency versions, not newer ones with unknown changes.
- A security auditor wants to verify that the toolchain used to compile a release matches the one specified in your security documentation.
- A regulated enterprise wants proof that sensitive data will be processed only by code that passed their review, not by a modified version.
The common thread: someone who didn't build the software needs to verify claims about how it was built.
At small scale, this is manageable through process. You can walk an auditor through your build system, show them your CI logs, explain your release procedures. They decide whether to trust your operational controls.
At scale, process-based trust breaks down. A package registry serves millions of artifacts to millions of consumers. A cloud platform runs workloads for thousands of customers. No one can personally audit every build. Consumers rely on claims: signed manifests, published hashes, attestations of policy compliance. But claims are not evidence. A compromised build server can produce a signed manifest for a tampered binary. A malicious insider can publish hashes for code that was never reviewed. The signature proves who made the claim, not that the claim is true.
Reproducible Builds: The Holy Grail
The theoretical ideal for software verification is reproducible builds: given the same source code, build environment, and build instructions, any party can recreate bit-for-bit identical artifacts. If compilation is fully deterministic, verification becomes straightforward. Compute the expected hash from the source, compare it to the artifact's hash, done.
This is the holy grail because it eliminates trust entirely. You don't need to trust the builder, the build infrastructure, or any claims about the build process. You trust math: if the hashes match, the artifact is correct. A distributed network of verifiers can independently rebuild and compare results. Reference measurements become meaningful because anyone can reproduce them.
The problem is that most toolchains are non-deterministic. Compilers embed timestamps. Parallel builds produce outputs in varying order. Linkers record file paths. Archive tools don't preserve consistent ordering. Achieving bit-for-bit identical output requires controlling all of this across your entire dependency tree. A single non-reproducible component anywhere in the chain breaks the guarantee. Projects like Reproducible Builds have made significant progress, but full reproducibility across an entire software ecosystem remains elusive.
But What About Nix?
Nix is often cited as a solution to the reproducibility problem, but this reflects a confusion between two different concepts: reproducible build environments and reproducible build outputs.
Nix provides reproducible build environments. Given a flake.lock or derivation, Nix guarantees you will build with the exact same inputs: the same source, the same dependencies, the same toolchain. This is a significant improvement over traditional package managers where dependency resolution can vary between machines or over time.
But reproducible inputs don't guarantee reproducible outputs. The same source built with the same compiler can still produce different binaries due to non-determinism in the compilation process itself. Nix makes the build environment deterministic, not the build output. Empirical studies of nixpkgs have found that roughly 9% of packages still produce non-identical outputs when rebuilt, due to embedded timestamps, build IDs, and other sources of non-determinism.
There's also a deeper issue: how do you trust Nix itself? A compromised Nix installation can produce bogus derivations. A malicious actor with access to your build machine can modify the Nix toolchain to inject code regardless of what your flake.lock specifies. The derivation hashes prove consistency with a particular Nix evaluation, but they don't prove that evaluation was honest. You've moved the trust problem from "trust the build" to "trust the Nix installation," which is progress, but not elimination of trust.
Attestable Builds: A Different Approach
Here is the core insight: if you can cryptographically verify how something was built, and what went into that build, then bind it to the outcome, then that gives you a lot of the guarantees you'd want out of reproducible builds.
The reframe: shift from asking "does this binary have hash X?" to asking "was this binary with hash X produced by process Y from sources Z in environment W?"
Reproducible Builds:
Source A ---[build]---> Binary X
Source A ---[rebuild]-> Binary Y
Verify: X == Y ? (must be bit-for-bit identical)
Problem: Fails if timestamps, file ordering, or optimizations differ
Attestable Builds:
Source A ----+
Deps B -+--[verified build environment]---> Binary X + Provenance
Toolchain C -+
Verify: Was the environment tamper-proof?
Were inputs A, B, C the expected ones?
Is X bound to that environment and those inputs?
This is a mouthful to state, but it turns out these verifications are much easier to achieve than bit-for-bit reproducible builds. You don't need to eliminate non-determinism from every compiler, linker, and archive tool in your dependency tree. You need to verify the process and bind the result to it.
This reframes the problem entirely. You're not trying to achieve deterministic compilation. You're trying to create a cryptographic chain of evidence that binds your output to auditable inputs through a process that can be independently verified.
A verifier checks cryptographic signatures and attestation reports. The verification is independent: anyone can verify without trusting the builder's claims.
Why Now: TEEs as Root of Trust
Why is the attestable builds approach possible now? How do we actually verify the statement "was this binary with hash X produced by process Y from sources Z in environment W?"
The answer is Trusted Execution Environments. TEEs provide a root of trust that is cryptographically attestable. Let's unpack what that means.
A TEE is a hardware-isolated execution environment where the CPU itself enforces protections that software cannot override. Even the operating system, hypervisor, and cloud provider with physical access to the machine cannot read or tamper with code running inside a TEE. Three primitives matter for attestable builds.
Isolation. Code runs in a hardware-protected environment. The host system (hypervisor, cloud provider, operators) cannot access memory or tamper with execution. Memory is encrypted with a per-VM key that only the CPU's security processor knows. When the hypervisor reads a guest's memory region, it gets ciphertext. The encryption key is generated by hardware, stored in hardware, and never exposed to any software.
Integrity. The hardware detects if the host tampers with guest memory. Substituting pages, replaying old data, or remapping addresses all trigger faults. The host cannot silently corrupt guest state. This is enforced by hardware checks on every memory access.
Attestation. The guest can prove to a remote party what code it's running. The CPU's security processor measures the initial guest image and signs a report with a key rooted in the silicon. A verifier can check this signature against the hardware vendor's certificate chain to confirm the report came from real hardware, not from software pretending to be a confidential VM.
The trust model becomes explicit. You trust the CPU vendor's silicon and firmware. You trust that their manufacturing process didn't embed backdoors. You trust that their key management practices keep root keys secure. You trust that the security processor firmware doesn't have exploitable vulnerabilities.
You don't trust the cloud provider's software stack. The hypervisor, host OS, management plane, orchestration systems, and monitoring agents are all outside the trust boundary. They can be compromised, malicious, or buggy without affecting the confidentiality or integrity of your workload.
You don't trust the cloud provider's employees. Administrators with root access to the hypervisor, datacenter technicians with physical access to servers, and anyone in the operational chain cannot read your memory or tamper with it undetected.
This is a much smaller trust surface than trusting the entire infrastructure. You're trading trust in your cloud provider's entire stack for trust in a CPU vendor's silicon. The attack surface shrinks from "everyone who can touch the infrastructure" to "the chip manufacturer and their firmware."
This creates the root of trust needed to make process verification meaningful. Without TEEs, "we verified the inputs" is just a claim. With TEEs, it's a hardware-signed assertion.
What Attestable Builds Give You
Attestable builds provide three properties that together close the verification gap.
Supply chain verification. The artifact was built from a specific source commit, with specific dependency versions, using a specific toolchain. Each input is cryptographically identified, not just named. The source is pinned to a git commit hash and tree hash. Dependencies are pinned via lockfiles with each package identified by cryptographic checksum. The toolchain (compiler, linker, build tools) is hashed. All of these become leaves in a Merkle tree, producing a single root hash that uniquely identifies the complete set of build inputs. If any input changes by a single byte, the root changes.
Build process integrity. The build executed in a hardware-isolated environment that can be remotely verified. The TEE attestation proves the build environment wasn't tampered with and that only the specified process ran. Networking is disabled after inputs are loaded. The build environment is isolated: nothing can be injected or exfiltrated during compilation. The TEE generates an attestation report proving the integrity of the loaded environment, signed by hardware and rooted in keys that only the CPU can access.
Verifiable chain of custody. Every link from source commit to running code is cryptographically bound. There's no gap where tampering could hide undetected. The build attestation includes the output measurement (the same hash that will appear in runtime attestation when this artifact runs in a confidential VM). Build attestation and runtime attestation reference the same measurement, closing the loop.
Verification is fast. Instead of rebuilding, a client checks cryptographic signatures and attestation reports. Verification becomes practical at scale.
The chain looks like this: source commit (signed by developers) to dependencies (pinned by hash) to toolchain (hashed) to Merkle root of all inputs to TEE attestation (hardware-signed proof of build environment) to output artifacts (signed by TEE-derived key) to runtime measurement (referenced in both build and runtime attestation). Every link is verifiable. The chain connects auditable source to running code with no gaps.
This doesn't require your builds to be reproducible. It requires them to be consistent enough that you can verify the inputs and trust the process. The TEE attestation substitutes for bit-for-bit determinism: instead of proving "anyone would get the same output," you prove "this specific process ran in a verified environment with these specific inputs."