Version: wac-cli 0.10.1 (wac-cli-x86_64-unknown-linux-musl, sha256 250c11762916ba733c7d22b62487580f21270ec9dde4f13460ea69d300e25406; also reproduced with wac-cli-aarch64-apple-darwin)
Platforms: linux/amd64 and darwin/arm64 — both affected identically.
Summary
With two or more --plug arguments, repeated wac plug invocations on byte-identical inputs produce byte-different outputs. All outputs are valid and semantically equivalent — the plug subcomponents are embedded verbatim and only their emission order in the outer component section varies, along with the corresponding instantiate indices. With a single plug the output is stable.
This makes wac plug unusable in content-addressed or reproducible build pipelines, where a composed artifact's hash is its identity.
Reproduction
for i in $(seq 1 20); do
wac plug socket.wasm --plug a.wasm --plug b.wasm -o out$i.wasm
done
sha256sum out*.wasm | awk '{print $1}' | sort | uniq -c
- 2 plugs / 20 runs → 2 distinct digests (8 / 12)
- 4 plugs / 30 runs → 16 distinct digests (4! = 24 permutations available)
All outputs are identical in size and all pass wasm-tools validate.
Command-line order is ignored
Running --plug a --plug b and --plug b --plug a 20 times each produces the same set of two digests in the same proportion. The output distribution is independent of argv order, so callers cannot work around this by ordering their arguments.
Localisation
Only the multi---plug path is affected:
wac compose with an explicit .wac source file is deterministic on the same inputs (20/20 and 21/21 identical at 2 and 4 dependencies).
- Chaining single-plug
wac plug invocations is deterministic (12/12).
This localises the issue to plug collection/iteration in wac plug rather than to composition or encoding generally.
wasm-tools objdump across 12 consecutive 4-plug runs shows the four plug subcomponents in 11 different orders, with the socket always emitted last. Each plug's payload is a verbatim byte-substring of the output in every run.
Likely cause
src/commands/plug.rs collects plugs into a std::collections::HashMap:
// Collect the plugs by their names
let mut plugs_by_name = std::collections::HashMap::<_, Vec<_>>::new();
which is then iterated directly:
for (name, plug_refs) in plugs_by_name {
Rust's HashMap has randomised iteration order, so the order in which plugs are registered and instantiated varies per process. crates/wac-graph/src/plug.rs takes an ordered Vec<PackageId> and appears unaffected.
Expected
Byte-identical output for byte-identical inputs — e.g. by preserving command-line order, or by iterating in a deterministic sorted order.
Contrast
The deprecated wasm-tools compose is deterministic on the same inputs (5/5 identical at both 2 and 4 dependencies). Since wac is the recommended replacement, reproducibility parity seems in scope.
Version: wac-cli 0.10.1 (
wac-cli-x86_64-unknown-linux-musl, sha256250c11762916ba733c7d22b62487580f21270ec9dde4f13460ea69d300e25406; also reproduced withwac-cli-aarch64-apple-darwin)Platforms: linux/amd64 and darwin/arm64 — both affected identically.
Summary
With two or more
--plugarguments, repeatedwac pluginvocations on byte-identical inputs produce byte-different outputs. All outputs are valid and semantically equivalent — the plug subcomponents are embedded verbatim and only their emission order in the outer component section varies, along with the correspondinginstantiateindices. With a single plug the output is stable.This makes
wac plugunusable in content-addressed or reproducible build pipelines, where a composed artifact's hash is its identity.Reproduction
All outputs are identical in size and all pass
wasm-tools validate.Command-line order is ignored
Running
--plug a --plug band--plug b --plug a20 times each produces the same set of two digests in the same proportion. The output distribution is independent of argv order, so callers cannot work around this by ordering their arguments.Localisation
Only the multi-
--plugpath is affected:wac composewith an explicit.wacsource file is deterministic on the same inputs (20/20 and 21/21 identical at 2 and 4 dependencies).wac pluginvocations is deterministic (12/12).This localises the issue to plug collection/iteration in
wac plugrather than to composition or encoding generally.wasm-tools objdumpacross 12 consecutive 4-plug runs shows the four plug subcomponents in 11 different orders, with the socket always emitted last. Each plug's payload is a verbatim byte-substring of the output in every run.Likely cause
src/commands/plug.rscollects plugs into astd::collections::HashMap:which is then iterated directly:
Rust's
HashMaphas randomised iteration order, so the order in which plugs are registered and instantiated varies per process.crates/wac-graph/src/plug.rstakes an orderedVec<PackageId>and appears unaffected.Expected
Byte-identical output for byte-identical inputs — e.g. by preserving command-line order, or by iterating in a deterministic sorted order.
Contrast
The deprecated
wasm-tools composeis deterministic on the same inputs (5/5 identical at both 2 and 4 dependencies). Sincewacis the recommended replacement, reproducibility parity seems in scope.