use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::process::Command;
pub const MSVC_DETERMINISM_RUSTFLAGS: &[&str] = &[
"-C codegen-units=1",
"-C link-arg=/Brepro",
"-C link-arg=/OPT:NOICF",
"-C link-arg=/INCREMENTAL:NO",
"-C link-arg=/DEBUG:NONE",
"-C strip=symbols",
];
pub fn merge_msvc_determinism_rustflags(base: &str) -> String {
let mut out = base.trim().to_string();
for &flag in MSVC_DETERMINISM_RUSTFLAGS {
let flag_tokens: Vec<&str> = flag.split_whitespace().collect();
let present = out
.split_whitespace()
.collect::<Vec<_>>()
.windows(flag_tokens.len())
.any(|w| w == flag_tokens.as_slice());
if present {
continue;
}
if !out.is_empty() {
out.push(' ');
}
out.push_str(flag);
}
out
}
pub fn host_is_windows_msvc() -> bool {
match crate::partial::detect_host_target() {
Ok(h) => crate::target::is_windows_msvc(&h),
Err(e) => {
tracing::warn!(
error = %e,
"host-target detection failed; treating host as non-windows-msvc and skipping MSVC determinism flags"
);
false
}
}
}
pub fn host_is_macos() -> bool {
match crate::partial::detect_host_target() {
Ok(h) => crate::target::is_darwin(&h),
Err(e) => {
tracing::warn!(
error = %e,
"host-target detection failed; treating host as non-macOS (Linux xar .pkg path is gated, not allow-listed)"
);
false
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeterminismState {
pub sde: i64,
pub compile_time_allowlist: Vec<(String, String)>,
pub runtime_allowlist: Vec<(String, String)>,
}
impl DeterminismState {
pub fn seed_from_commit(commit_ts: i64) -> Result<Self> {
if commit_ts < 0 {
anyhow::bail!(
"commit_ts must be non-negative (got {}); a corrupted commit graph or future-bug? \
Negative SOURCE_DATE_EPOCH would propagate to child processes and be \
misinterpreted by shells/build tools.",
commit_ts
);
}
let mut installer_allow: Vec<(&str, &str)> = vec![
(
"*.msi",
"WiX candle/light regenerates a random SummaryInformation PackageCode GUID and stamps wall-clock Created/LastModified into the MSI summary-information stream, independent of SOURCE_DATE_EPOCH (wixtoolset/issues#8978); not byte-reproducible — proven by stage-msi::msi_is_byte_reproducible_across_time. Built natively on the windows determinism shard under A′.",
),
(
"*.dmg",
"hdiutil writes a fresh per-segment SegmentID GUID into the UDIF koly trailer every run and pins no SOURCE_DATE_EPOCH; not byte-reproducible — proven by stage-dmg::dmg_is_byte_reproducible_across_time. Built natively on the macos determinism shard under A′.",
),
(
"*.flatpak",
"flatpak build-bundle wraps an OSTree commit whose metadata (commit object timestamp + per-object headers) is not byte-stable across runs even at a fixed SOURCE_DATE_EPOCH; empirically confirmed non-reproducible via two-build cmp",
),
(
"*.deb",
"nfpm GPG-signs the deb (_gpgorigin ar member); the signature embeds a non-pinnable creation time / randomized salt and is not byte-reproducible even at a fixed SOURCE_DATE_EPOCH. The package body (debian-binary, control.tar.gz, data.tar.gz) IS byte-reproducible — gated by stage-nfpm::signed_deb_body_is_byte_reproducible_across_time — and the signature is verified cryptographically (gpg --verify), not by byte-equality.",
),
(
"*.rpm",
"nfpm GPG-signs the rpm (RPM signature header); the signature embeds a non-pinnable creation time / randomized salt and is not byte-reproducible even at a fixed SOURCE_DATE_EPOCH. The package body (main header + cpio payload, i.e. everything after the signature header) IS byte-reproducible — gated by stage-nfpm::signed_rpm_body_is_byte_reproducible_across_time — and the signature is verified cryptographically (gpg --verify), not by byte-equality.",
),
];
if host_is_macos() {
installer_allow.push((
"*.pkg",
"macOS-native pkgbuild stamps a wall-clock xar TOC and ignores SOURCE_DATE_EPOCH; not byte-reproducible — proven by stage-pkg::native_pkgbuild_pkg_is_byte_reproducible_across_time. Built natively on the macos determinism shard under A′ (the Linux xar/mkbom/cpio .pkg path is proven reproducible and gated, never allow-listed)",
));
}
let sbom_allow: &[(&str, &str)] = &[
(
"*.cdx.json",
"CycloneDX SBOM embeds a random serialNumber UUID and a generation timestamp (syft does not honor SOURCE_DATE_EPOCH for it); not byte-reproducible across runs",
),
(
"*.spdx.json",
"SPDX SBOM embeds a documentNamespace UUID and a created timestamp; not byte-reproducible across runs",
),
(
"*.sbom.json",
"SBOM document embeds a per-document unique identifier and generation timestamp; not byte-reproducible across runs",
),
];
let mut compile_time_allowlist: Vec<(String, String)> = Vec::new();
for (pattern, reason) in installer_allow.iter().chain(sbom_allow) {
compile_time_allowlist.push(((*pattern).into(), (*reason).into()));
compile_time_allowlist.push((
format!("{}.sha256", pattern),
format!("derivative of {pattern}: {reason}"),
));
}
compile_time_allowlist.push((
"artifacts.json".into(),
"anodize dist manifest aggregating every artifact's size+digest \
(including allow-listed non-deterministic SBOMs/signatures); a derivative \
signal — each indexed artifact is drift-checked independently"
.into(),
));
Ok(Self {
sde: commit_ts,
compile_time_allowlist,
runtime_allowlist: Vec::new(),
})
}
pub fn export_env(&self, cmd: &mut Command) {
cmd.env("SOURCE_DATE_EPOCH", self.sde.to_string());
}
pub fn resolve_reason(&self, artifact: &str) -> Option<&str> {
for (name, reason) in &self.compile_time_allowlist {
if matches_artifact_pattern(name, artifact) {
return Some(reason.as_str());
}
}
for (name, reason) in &self.runtime_allowlist {
if matches_artifact_pattern(name, artifact) {
return Some(reason.as_str());
}
}
None
}
pub fn append_runtime(&mut self, artifact: String, reason: String) {
self.runtime_allowlist.push((artifact, reason));
}
}
fn matches_artifact_pattern(pattern: &str, artifact: &str) -> bool {
if let Some(suffix) = pattern.strip_prefix('*') {
return artifact.ends_with(suffix);
}
pattern == artifact
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn msvc_rustflags_const_matches_cargo_config() {
let cargo_config = include_str!("../../../.cargo/config.toml");
for &flag in MSVC_DETERMINISM_RUSTFLAGS {
let (lead, rest) = flag.split_once(' ').expect("each flag is two tokens");
let toml_pair = format!("\"{lead}\", \"{rest}\"");
assert!(
cargo_config.contains(&toml_pair),
".cargo/config.toml must carry `{toml_pair}` to stay aligned with \
MSVC_DETERMINISM_RUSTFLAGS; the two drifted"
);
}
}
#[test]
fn merge_msvc_into_empty_yields_full_set() {
let merged = merge_msvc_determinism_rustflags("");
for &flag in MSVC_DETERMINISM_RUSTFLAGS {
assert!(
merged.contains(flag),
"merged set must contain `{flag}`. got={merged}"
);
}
assert!(
merged.contains("/Brepro"),
"the COFF TimeDateStamp fix must be present"
);
}
#[test]
fn merge_msvc_preserves_existing_flags_and_appends() {
let base = "-C linker=link.exe --remap-path-prefix=/w=/build";
let merged = merge_msvc_determinism_rustflags(base);
assert!(
merged.starts_with(base),
"existing flags must be preserved verbatim. got={merged}"
);
assert!(
merged.contains("-C link-arg=/Brepro"),
"MSVC flags must be appended. got={merged}"
);
}
#[test]
fn merge_msvc_is_idempotent_no_duplicate_brepro() {
let once = merge_msvc_determinism_rustflags("");
let twice = merge_msvc_determinism_rustflags(&once);
assert_eq!(
once, twice,
"merging an already-merged set must not duplicate flags"
);
assert_eq!(
twice.matches("/Brepro").count(),
1,
"/Brepro must appear exactly once after a double merge. got={twice}"
);
}
#[test]
fn host_is_windows_msvc_matches_real_host() {
let expected = crate::partial::detect_host_target()
.map(|h| crate::target::is_windows_msvc(&h))
.unwrap_or(false);
assert_eq!(host_is_windows_msvc(), expected);
}
#[test]
fn sde_from_commit_timestamp_is_idempotent() {
let s = DeterminismState::seed_from_commit(1_715_000_000).expect("non-negative");
assert_eq!(s.sde, 1_715_000_000);
let s2 = DeterminismState::seed_from_commit(1_715_000_000).expect("non-negative");
assert_eq!(s, s2);
}
#[test]
fn cargo_crate_is_gated_not_allowlisted() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("anodizer-0.2.1.crate").is_none());
assert!(s.resolve_reason("anodizer-core-0.11.3.crate").is_none());
assert!(
s.resolve_reason("anodizer-core-0.11.3.crate.sha256")
.is_none()
);
}
#[test]
fn rpm_and_deb_are_allowlisted_due_to_gpg_signing() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
let rpm = s.resolve_reason("foo-1.0.rpm").expect("matches *.rpm");
assert!(
rpm.contains("signature") || rpm.contains("GPG"),
"rpm reason must cite the signature: {rpm}"
);
let deb = s
.resolve_reason("foo_1.0_amd64.deb")
.expect("matches *.deb");
assert!(
deb.contains("signature") || deb.contains("GPG"),
"deb reason must cite the signature: {deb}"
);
assert!(s.resolve_reason("foo-1.0.rpm.sha256").is_some());
assert!(s.resolve_reason("foo_1.0_amd64.deb.sha256").is_some());
}
#[test]
fn snap_is_gated_not_allowlisted() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("probe_1.2.3_amd64.snap").is_none());
assert!(s.resolve_reason("probe_1.2.3_amd64.snap.sha256").is_none());
}
#[test]
fn apk_is_signed_but_gated() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("foo_1.0_amd64.apk").is_none());
assert!(s.resolve_reason("foo_1.0_amd64.apk.sha256").is_none());
}
#[test]
fn compile_time_allowlist_resolves_for_flatpak() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
let reason = s
.resolve_reason("anodizer_0.9.1_linux_amd64.flatpak")
.expect("matches *.flatpak");
assert!(reason.contains("OSTree"));
assert!(
s.resolve_reason("anodizer_0.9.1_linux_amd64.flatpak.sha256")
.is_some()
);
}
#[test]
fn pkg_allowlist_is_host_keyed() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
let resolved = s.resolve_reason("anodizer-0.2.1.pkg").is_some();
assert_eq!(
resolved,
host_is_macos(),
"`.pkg` must be allow-listed iff the host is macOS (Linux xar path is gated)"
);
if !host_is_macos() {
assert!(
s.resolve_reason("anodizer-0.2.1.pkg").is_none(),
"Linux xar .pkg path is proven reproducible and must be gated"
);
assert!(s.resolve_reason("anodizer-0.2.1.pkg.sha256").is_none());
}
}
#[test]
fn appbundle_is_gated_not_allowlisted() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("anodizer_arm64.app").is_none());
assert!(s.resolve_reason("anodizer_amd64.app").is_none());
assert!(s.resolve_reason("anodizer_arm64.app.sha256").is_none());
}
#[test]
fn nsis_installer_is_gated_not_allowlisted() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("anodizer_x64-setup.exe").is_none());
assert!(s.resolve_reason("anodizer_x64_setup.exe").is_none());
assert!(
s.resolve_reason("anodizer_arm64-setup.exe.sha256")
.is_none()
);
}
#[test]
fn raw_windows_binary_is_not_allowlisted_or_misclassified() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("anodizer.exe").is_none());
assert!(s.resolve_reason("anodizer.exe.sha256").is_none());
}
#[test]
fn msi_and_dmg_reasons_cite_pending_shard_proof() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
let msi = s
.resolve_reason("anodizer-0.2.1.msi")
.expect("matches *.msi");
assert!(
msi.contains("msi_is_byte_reproducible_across_time"),
"msi reason must cite the pending shard test: {msi}"
);
let dmg = s
.resolve_reason("anodizer-0.2.1.dmg")
.expect("matches *.dmg");
assert!(
dmg.contains("dmg_is_byte_reproducible_across_time"),
"dmg reason must cite the pending shard test: {dmg}"
);
}
#[test]
fn compile_time_allowlist_resolves_for_sbom_documents() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
for name in [
"cfgd-0.4.0-linux-amd64.tar.gz.cdx.json",
"cfgd-0.4.0-linux-amd64.tar.gz.spdx.json",
"cfgd-0.4.0-linux-amd64.tar.gz.sbom.json",
] {
assert!(
s.resolve_reason(name).is_some(),
"SBOM document {name} must be allow-listed"
);
}
}
#[test]
fn compile_time_allowlist_resolves_for_sbom_checksum_sidecars() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
let reason = s
.resolve_reason("cfgd-0.4.0-linux-amd64.tar.gz.cdx.json.sha256")
.expect("matches *.cdx.json.sha256");
assert!(reason.contains("derivative of"));
}
#[test]
fn compile_time_allowlist_resolves_for_artifacts_manifest() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(
s.resolve_reason("artifacts.json").is_some(),
"the dist manifest aggregates non-deterministic artifact sizes/digests"
);
assert!(s.resolve_reason("config.json").is_none());
assert!(s.resolve_reason("metadata.json").is_none());
}
#[test]
fn nondeterministic_allowlist_compile_time_wins_on_collision() {
let mut s = DeterminismState::seed_from_commit(0).expect("non-negative");
s.append_runtime(
"*.flatpak".into(),
"operator escape (wrong runtime reason)".into(),
);
let reason = s
.resolve_reason("anodizer_0.9.1_linux_amd64.flatpak")
.unwrap();
assert!(
reason.contains("OSTree"),
"compile-time reason takes precedence"
);
}
#[test]
fn nondeterministic_allowlist_serializes_with_both_categories() {
let mut s = DeterminismState::seed_from_commit(0).expect("non-negative");
s.append_runtime("foo.bin".into(), "tool-bug-1234".into());
let json = serde_json::to_string(&s).unwrap();
assert!(json.contains("compile_time_allowlist"));
assert!(json.contains("runtime_allowlist"));
assert!(json.contains("foo.bin"));
}
#[test]
fn export_env_sets_source_date_epoch() {
let s = DeterminismState::seed_from_commit(1_715_000_000).expect("non-negative");
let mut cmd = Command::new("true");
s.export_env(&mut cmd);
let env_vars: Vec<(_, _)> = cmd
.get_envs()
.filter_map(|(k, v)| v.map(|v| (k.to_owned(), v.to_owned())))
.collect();
let sde_entry = env_vars.iter().find(|(k, _)| k == "SOURCE_DATE_EPOCH");
assert!(sde_entry.is_some());
assert_eq!(sde_entry.unwrap().1, "1715000000");
}
#[test]
fn resolve_reason_returns_none_for_unrecognized() {
let s = DeterminismState::seed_from_commit(0).expect("non-negative");
assert!(s.resolve_reason("unrelated.txt").is_none());
}
#[test]
fn seed_from_commit_accepts_zero() {
let s = DeterminismState::seed_from_commit(0).expect("zero is non-negative");
assert_eq!(s.sde, 0);
}
#[test]
fn seed_from_commit_accepts_positive() {
let s = DeterminismState::seed_from_commit(1_715_000_000).expect("non-negative");
assert_eq!(s.sde, 1_715_000_000);
}
#[test]
fn seed_from_commit_rejects_negative() {
let err = DeterminismState::seed_from_commit(-1).expect_err("negative must error");
let msg = format!("{err:#}");
assert!(
msg.contains("non-negative") && msg.contains("-1"),
"error must name the bad input and the constraint: {msg}"
);
}
}