Reproducible Builds
dvb-WarpPool produces bit-identical release binaries: anyone with the
same source commit, the same Rust toolchain, and a Linux-x86_64 host can
rebuild the release and compare it against sha256. This closes the gap
between "we trust the build maintainer" and "we only trust the source
code".
Why
- Supply-chain resilience — if someone pushes a compromised binary, they either changed the source (visible diff) or patched the binary after the fact (sha256 mismatch).
- Cosign + SLSA provenance say "the build came from this workflow". Reproducible builds additionally say "the build is the source".
- Audit-friendly — pool operators can periodically verify that the pulled image matches the tagged source.
How we achieve it
Cargo profile (in Cargo.toml)
[profile.release]
lto = "fat" # single-threaded LTO, no parallelism drift
codegen-units = 1 # a single LLVM pass
strip = "symbols" # removes debug-info drift
panic = "abort"
incremental = false # explicit
lto = "thin" was deliberately switched to "fat" — thin-LTO is
faster (parallel) but can produce byte drift between host configurations.
Toolchain pin
rust-toolchain.toml pins the rustc + cargo
version. CI and local verification use exactly this version.
Path remapping
Build-time paths ($CARGO_HOME, $GITHUB_WORKSPACE, $HOME) would
otherwise end up embedded as debug info in the binary. We remap them to
generic strings:
RUSTFLAGS="\
--remap-path-prefix=$WORKSPACE=/repo \
--remap-path-prefix=$HOME/.cargo/registry=/cargo/registry \
--remap-path-prefix=$HOME/.cargo/git=/cargo/git"
SOURCE_DATE_EPOCH
The release.yml workflow sets SOURCE_DATE_EPOCH from the commit
timestamp. deb/rpm archives use it for mtime fields instead of now().
(The Rust binary itself currently does not embed timestamps, but wrappers
and installers do.)
Cargo.lock
We commit Cargo.lock and the build uses --locked. This also pins
every transitive dependency version.
Verifying — CI side
Every push to main triggers
repro.yml:
build-a ─┐
├─► verify-determinism (sha256 comparison)
build-b ─┘
Two separate runners build the same dvb-warppool-daemon binary. On
drift the workflow fails and prints both sha256s.
Verifying — end-user side
scripts/verify-reproducible.sh
is a bash script that:
- determines the
latestrelease tag (or an explicitly passed tag) - sets
SOURCE_DATE_EPOCHfrom the commit timestamp - locally runs
cargo build --release -p dvb-warppool-daemon --lockedwith the path-remapping flags - downloads the GitHub release asset
- compares both sha256s and exits 0 (match) or 1 (drift)
# Verify the latest release
./scripts/verify-reproducible.sh
# A specific version
./scripts/verify-reproducible.sh v0.1.0
# Keep build artifacts for debugging
./scripts/verify-reproducible.sh --keep-target
Limitations
- Linux-x86_64 only — macOS
.dmgand Windows.msiare not byte-deterministic (code signing, installer metadata). Cosign signatures cover those. - Glibc drift — if a pool operator builds on an old distro (Debian Buster) while the CI runner uses Ubuntu Latest, glibc drift can occur. Workaround: build in the same Docker image the CI uses, or use a static MUSL build (phase 8c).
- LLVM version drift — rust-toolchain.toml pins rustc, but the LLVM version shipped with it varies minor across rustup channels. We accept this as a known limitation.
When the build drifts
- Local HEAD vs tag commit:
git rev-parse HEAD git rev-parse refs/tags/<TAG> git checkout <TAG> # if different - Match the Rust toolchain:
rustup show active-toolchain # should match rust-toolchain.toml - Check RUSTFLAGS:
echo "$RUSTFLAGS" # must contain the three --remap-path-prefix entries - Glibc version:
ldd --version # CI uses Ubuntu LTS — a minor mismatch is OK, a major one breaks it - If everything matches but drift persists: please file an issue with
both sha256s and the
ldd --versionoutput.