Overview
Every Tinfoil enclave boots from the same confidential VM (CVM) image, published from the open-sourcecvmimage repository.
Alongside the firmware, it makes up most of the trusted computing
base (TCB): the kernel, the initrd, and the root filesystem that runs the reverse proxy, the
container runtime, and your workload.
The image is designed around two properties:
- Small, declared TCB. The image is built additively from an empty tree. Every file that ships is declared in the repository, and every external input is pinned by hash.
- Byte-for-byte reproducible. The build is hermetic. Anyone can rebuild the image from the release commit on their own machine and obtain artifacts identical to the ones Tinfoil ships.
How this relates to the transparency log
Tinfoil’s primary verification path does not require you to rebuild anything. GitHub Actions builds each release and publishes the resulting measurements to Sigstore, and the SDKs compare the enclave’s attestation against that signed record at connection time. Reproducible builds close the remaining gap in that model: the transparency log proves which binary GitHub built, but not that the binary matches the source it claims to come from. A reproducible build lets you check that link independently. If your rebuild produces the same artifacts as the release, the measurement in the transparency log is the measurement of the published source, not of something a compromised builder could have inserted. As a defense in depth, the Sigstore entries also prove that every build was done on an isolated GitHub worker without Tinfoil access. The two mechanisms are complementary. Connection-time verification relies on the transparency log and is fast enough to run on every request. Auditing uses the reproducible build to confirm the log entries are honest.Trusted computing base
The measured image contains only what is listed below. Anything not declared in the repository does not ship.
Notable properties of the Tinfoil CVM image:
- Additive assembly.
nix/rootfs.nixstarts from an empty tree and installs only declared paths. Ubuntu packages are extracted into staging trees; maintainer scripts do not run, and manuals, headers, service units, and other undeclared files never enter the image. - Stateless disk. The image is a fixed 2 GiB read-only EROFS root partition plus its dm-verity hash partition. There is no writable partition and no EFI system partition. QEMU supplies the kernel, initrd, and firmware directly.
- Fail-closed runtime policy. The measured configuration in
image/rootfs/disables inter-container communication, prevents runtime kernel module loading, restricts container capabilities, and installs a default-deny firewall. Seedocs/runtime-policy.md.
Keeping the TCB small
The trusted computing base (TCB) is everything that can break confidentiality if it is wrong. The host and hypervisor are outside it by design, so what remains is the firmware, the kernel, and the guest userspace. The image is built to keep that set small and legible. Purpose-built, not general-purpose. The image does one job: boot, attest, and run containers with optional GPU access. The kernel includes only what that job needs, all of it built in at compile time. Userspace has no systemd, udev, shell, or package manager; a small supervisor orchestrates a handful of fixed services. Remove whole categories, not individual pieces. The kernel has no drivers for hardware the VM does not have, no legacy protocols, no 32-bit compatibility, and only the filesystems the image uses. Anything that would let running code change the kernel is switched off. Userspace follows the same rule: Docker and the NVIDIA stack ship only the parts the workload needs. Trust nothing from the host. Whatever the hypervisor provides at boot is either measured and covered by attestation, or reduced to a fixed shape with hardcoded possible values. The image does not read hardware description tables from the host, does not use DHCP, does not parse disk metadata to find its root, and has no console. For instance, disks are found by their fixed address, and the only network settings the host can supply are an address and a gateway. Lock down what remains. Once the GPU driver is loaded, module loading is disabled for good before any workload starts. Every Tinfoil service runs with a fixed allowlist of system calls and the minimum privileges it needs, so a compromised service cannot reach into the kernel or its neighbors.What gets measured
A release produces four artifacts:tinfoilcvm.vmlinuz, tinfoilcvm.initrd, tinfoilcvm.raw,
and tinfoilcvm.roothash. These are the values bound into attestation:
- The kernel and initrd are measured by the CPU at boot and appear in the hardware attestation report.
- The root hash is the dm-verity root of the disk image. It is passed on the kernel command line, so it is measured too, and the kernel rejects any disk block that does not match.
cvm-version field in your tinfoil-config.yml pins.
Build hermeticity
Every builder, whether GitHub’s CI, a release runner, or an auditor, installs Nix through the samenix/install.sh script. It installs a pinned official Nix release, verifies its checksum,
and configures the daemon to build artifact hermetically using a clean sandbox.
The only network access permitted at evaluation time is the hash-pinned
Nixpkgs archive.
External inputs enter the build only through fixed-output derivations: fetches that declare their
expected hash before the sandbox permits network access. A derivation without a declared hash
builds with no network at all.
Reproducing a release
You need an x86_64 Linux host with systemd,sudo, curl, tar, and xz. The build runs
inside the Nix sandbox, so the host distribution does not affect the result.
1
Check out the release commit
2
Install the pinned Nix release
3
Build the shipping image
4
Compare against the published release
Auditing the inputs
Reproducing the build proves the artifacts match the source. Two further checks let you review what the source pulls in. Review every external input. Each byte that enters the build from outside the repository is declared with its URL and hash in one of these files:
These files are the readable inventory. To confirm nothing else in the build graph fetches from
the network, the repository’s
audit guide
shows how to enumerate every fixed-output derivation from the instantiated graph and compare it
against the declarations above.
List every file in the root filesystem. Build the rootfs archive on its own and inspect it:
image/rootfs/.
For a fuller walkthrough of the build graph and the ownership of each output, see
docs/build.md in the
repository.
