Skip to content

Make Linux desktop release artifacts bit-for-bit reproducible #6616

Description

@Giszmo

I rebuilt desktop-v0.5.18 (Buzz_0.5.18_amd64.AppImage + .deb) following
.github/workflows/release.yml exactly: same digest-pinned ubuntu:24.04 container, hermit
toolchain (rust 1.95.0 / node 24.15.0 / pnpm 11.4.0), CI workspace path /__w/buzz/buzz,
cargo update --workspace, appimagetool 1.9.1 + pinned type2 runtime, fix-appimage.sh repack.

Result: not bit-for-bit, but remarkably close — every bundled file reproduces exactly
except the main buzz-desktop binary. All six sidecars (buzz, buzz-acp, buzz-agent,
buzz-backend-kubernetes, buzz-dev-mcp, git-credential-nostr) are bit-identical.

Official vs rebuild:

  • AppImage d04ace73… vs ff163ec2…; deb 7ac74a1c… vs f5cc9840…
  • Main binary: identical size (200,174,032). .text/.data identical — the compiled code
    reproduces. Remaining diffs: .note.gnu.build-id; .strtab/.symtab ThinLTO anonymous
    symbol hashes (anon.<md5>.NNN.llvm.<id>); ~19.5 KB in .rodata consisting of
    'sha256-…' CSP inline-script hash strings (Tauri embeds per-build random material in its
    injected init scripts, so the hashes churn every build).
  • AppImage runtime segment: only the 16-byte .digest_md5 differs (derived from payload).
  • linuxdeploy picks the top-level buzz-desktop.png symlink target nondeterministically
    (official: 32x32, rebuild: 128x128).
  • deb: md5sums line ordering is nondeterministic; per-file md5s match except buzz-desktop.
    tar/gzip metadata (mtimes) differ — no SOURCE_DATE_EPOCH anywhere in the pipeline.

Suggested fixes, roughly in impact order:

  1. Export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) in the release job (normalizes
    squashfs/tar/gzip timestamps).
  2. Chase the Tauri CSP-hash randomness upstream (tauri codegen inline-script generation) or
    pin whatever seed feeds it — this plus build-id is the whole main-binary diff.
  3. Deterministic icon symlink and sorted md5sums.
  4. Document the rebuild procedure (container digest, workspace path, throwaway
    TAURI_SIGNING_PRIVATE_KEY needed because createUpdaterArtifacts refuses to run unsigned)
    so third parties can verify releases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions