Files
git.stella-ops.org/docs/releases/REPRODUCIBLE_BUILDS.md
2026-01-28 02:30:48 +02:00

4.4 KiB

Reproducible Builds

Stella Ops releases are reproducible: given the same source code and build environment, anyone can produce byte-identical artifacts.

Overview

Reproducible builds provide:

  1. Verifiability - Anyone can verify that released binaries match source code
  2. Trust - No need to trust the build infrastructure
  3. Auditability - Build process can be independently audited
  4. Security - Compromised builds can be detected

How It Works

SOURCE_DATE_EPOCH

All timestamps in build outputs use the SOURCE_DATE_EPOCH environment variable instead of the current time. This is set to the git commit timestamp:

export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)

Deterministic Build Settings

The following MSBuild properties ensure deterministic .NET builds:

<!-- src/Directory.Build.props -->
<PropertyGroup>
  <Deterministic>true</Deterministic>
  <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
  <PathMap>$(MSBuildProjectDirectory)=/src/</PathMap>
  <EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

Pinned Dependencies

All dependencies are pinned to exact versions in Directory.Packages.props:

<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />

Containerized Builds

Release builds run in containerized environments with:

  • Fixed base images
  • Pinned tool versions
  • Isolated network (no external fetches during build)

Reproducing a Build

Prerequisites

  • .NET SDK (version in global.json)
  • Git
  • Docker (optional, for containerized builds)

Steps

  1. Clone the repository
git clone https://git.stella-ops.org/stella-ops.org/git.stella-ops.org.git
cd git.stella-ops.org
  1. Checkout the release tag
git checkout v1.2.3
  1. Set SOURCE_DATE_EPOCH

Get the value from the release evidence pack manifest.json:

export SOURCE_DATE_EPOCH=1705315800

Or compute from git:

export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)
  1. Build
# Using make
make release

# Or using dotnet directly
dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj \
  --configuration Release \
  --runtime linux-x64 \
  --self-contained true \
  /p:Deterministic=true \
  /p:ContinuousIntegrationBuild=true \
  /p:SourceRevisionId=$(git rev-parse HEAD)
  1. Compare checksums
sha256sum dist/stella-* | diff - path/to/evidence-pack/checksums/SHA256SUMS

CI Verification

The CI pipeline automatically verifies reproducibility:

  1. Builds artifacts twice with the same SOURCE_DATE_EPOCH
  2. Compares checksums between builds
  3. Fails if checksums don't match

See .gitea/workflows/verify-reproducibility.yml.

What Can Cause Non-Reproducibility

Timestamps

  • Problem: Build tools embed current time
  • Solution: Use SOURCE_DATE_EPOCH

Path Information

  • Problem: Absolute paths embedded in binaries/PDBs
  • Solution: Use PathMap to normalize paths

Random Values

  • Problem: GUIDs, random seeds
  • Solution: Use deterministic generation or inject via DI

Unordered Collections

  • Problem: Dictionary/HashSet iteration order varies
  • Solution: Use ImmutableSortedDictionary or explicit sorting

External Resources

  • Problem: Network fetches return different content
  • Solution: Pin dependencies, use hermetic builds

Compiler/Tool Versions

  • Problem: Different tool versions produce different output
  • Solution: Pin all tool versions in global.json and CI

Debugging Non-Reproducible Builds

Compare binaries

# Install diffoscope
pip install diffoscope

# Compare two builds
diffoscope build1/stella.dll build2/stella.dll

Check for timestamps

# Look for embedded timestamps
strings stella.dll | grep -E '20[0-9]{2}-[0-9]{2}'

Check PDB content

# Examine PDB for path information
dotnet tool install -g dotnet-symbol
dotnet symbol --symbols stella.dll

Verification in Evidence Pack

The Release Evidence Pack includes:

  1. SOURCE_DATE_EPOCH in manifest.json
  2. Source commit for exact source checkout
  3. Checksums for comparison
  4. Build instructions in VERIFY.md