Skip to content

Reproduce Builds Locally

Target audience: Developers, Security auditors Goal: Verify bit-for-bit reproducibility of StageX builds.

Prerequisites

  • Podman (or Docker) installed
  • git, curl, jq
  • GPG (for signature verification)
  • Hardware: see System Requirements for disk, RAM, and CPU specs by workload

Clone the StageX Repo

git clone --depth=1 https://codeberg.org/stagex/stagex.git
cd stagex

Release branches follow release/YYYY.MM.<revision>:

git branch --list 'release/*'
git checkout release/2026.05.0

Replace release/2026.05.0 with the latest available tag.

Preseed the Build Cache

Pull the last published OCI layers into out/ to seed the build cache without rebuilding from source:

make preseed

make preseed runs docker pull internally (preseed.sh line 11). If using Podman, set ENGINE=podman or symlink dockerpodman.

This downloads published images and extracts them into out/<package>/index.json plus blob directories. Subsequent incremental builds reuse cached layers.

To build from source instead (no preseed), skip this step and go directly to Build.

Build a Single Package

Build pallet-rust without building the full tree:

make pallet-rust

Output lands in out/pallet-rust/:

out/pallet-rust/
├── index.json
└── blobs/
    └── sha256/
        ├── <digest-1>
        └── <digest-2>

The first run also generates out/targets.mk via src/targets.py, which defines the build rules.

Get the Output Digest

Extract the image manifest digest — this is what gets published in digests/pallet.txt:

INDEX_DIGEST=$(jq -r '.manifests[0].digest | split(":")[1]' out/pallet-rust/index.json)
LOCAL_DIGEST=$(jq -r '.manifests[0].digest | split(":")[1]' "out/pallet-rust/blobs/sha256/$INDEX_DIGEST")
echo "$LOCAL_DIGEST"

This follows the index reference to the multi-arch manifest and retrieves the sha256 digest of the built image — the same logic used by src/digests.py.

Compare Against Published Digests

PUBLISHED_DIGEST=$(curl -s https://codeberg.org/stagex/stagex/raw/branch/main/digests/pallet.txt \
  | grep pallet-rust | awk '{print $1}')
echo "Published: $PUBLISHED_DIGEST"
echo "Local:     $LOCAL_DIGEST"

Check for a match:

[ "$LOCAL_DIGEST" = "$PUBLISHED_DIGEST" ] && echo "MATCH" || echo "MISMATCH"

Expected format (digests/pallet.txt):

<64-hex-chars> pallet-rust

A MATCH means your build is bit-for-bit identical to the published release — you've independently reproduced the maintainer's build.

Verify with Published Signatures

Once your local digest matches the published digest, verify the GPG signatures on that digest as described in Verify Multi-Signature Attestations.

Cross-Machine Verification

Build on a different machine using the same release branch and compare digests. StageX maintainers build across diverse hardware (MAINTAINERS):

Maintainer Hardware
Lance Vick AMD Ryzen Threadripper 2990WX, AMD EPYC 7502P
Danny Grove Intel Core i7-6700K, AMD Ryzen 7 7840U
Anton Livaja AMD Ryzen Threadripper 2970WX, AMD EPYC Milan

All produce identical digests for the same release.

Troubleshooting

Problem Cause Fix
Build fails mid-way Missing source tarball make fetch && make pallet-rust
command not found: docker in preseed preseed.sh hardcodes docker pull alias docker=podman or install docker-compatible CLI
command not found: jq jq not installed apt install jq or brew install jq
Out of disk space out/ contains old builds rm -rf out/ and rebuild
Digest mismatch after preseed Preseed pulled wrong release git log --oneline to confirm branch; re-preseed
No rule to make target 'pallet-rust' out/targets.mk not yet generated Run make pallet-rust once — targets auto-generate on first invocation
Signature verification fails Missing maintainer GPG keys See Verify Multi-Signature Attestations

See Also