What
cargo deny check bans emits warning[duplicate] for 58 distinct crates / 126 lock entries. The check passes — multiple-versions = "warn" in deny.toml is deliberate. Nothing is failing; this issue is the curated triage that comment promises ("Revisit as its own change, with the skip list justified entry by entry").
Measured 2026-07-31 on main @ c2765bc, on Windows. The windows-sys family will differ on Linux CI — re-measure there before acting on bucket 2.
Not caused by committing Cargo.lock
Worth writing down because it was the first guess. cargo deny reads the lock, but duplicates live in the dependency graph — without a committed lock, CI resolves one and reports the same set. Committing it (#66) made them visible, not real, and is correct for this repo: every crate is publish = false, so cargo audit in CI now audits the exact graph that ships. Keep it committed.
Bucket 1 — ours, actionable
1. serde_jcs 0.1.0 vs 0.2.0 — delete, highest value
dpp-node declares it and no source file in the repo uses it. Only two lines mention it:
Cargo.toml:81 — serde_jcs = "0.1" # RFC 8785 JSON Canonicalisation — audit-chain hashing
crates/dpp-node/Cargo.toml:63 — serde_jcs = { workspace = true }
crates/dpp-types/Cargo.toml:19 records that this was refactored out ("this crate no longer needs serde_jcs/sha2/hex") — the dependency was left behind. All of dpp-core is on 0.2 (dpp-core/Cargo.toml:64).
Why this is not hygiene. JCS is the canonicalisation the signature is computed over. Two canonicalisers in one signing binary is a hazard the moment someone reaches for the wrong use, and the failure mode is bytes we sign that core's verifier does not reproduce. It is dead today, which is exactly why removing it is free — and why leaving it is a trap with no upside.
Verify before deleting: grep -rn serde_jcs crates/ cli/ should return only the two manifest lines above.
2. zip 0.6.6 vs 8.6.0
dpp-integrator declares zip = "0.6" directly; its own calamine 0.36.1 pulls zip 8.6.0. Bumping our direct dependency to 8.x unifies them. 0.6 is also four years stale.
Check the API delta — zip 0.6 → 8.x crosses several majors, so this is a real edit, not a version-string bump.
3. image 0.23.14 vs 0.24.9
dpp-resolver uses image 0.24 directly; qrcode 0.12 drags in image 0.23. Not fixable by a bump — qrcode 0.12 is effectively unmaintained (last release 2021). This is a "pick a maintained QR crate, or drop qrcode's image feature" decision, and it is the least urgent of the three. Leave it until the QR path is open for another reason.
Bucket 2 — transitive, nothing to do
windows-sys ×5, hashbrown ×5, rand / getrandom / bitflags / base64 ×3, plus ~50 more at ×2. The AWS SDK, wasmtime, sqlx, axum and testcontainers each carry their own trees and do not align. Wait for upstream.
Do not add a skip list yet
A skip list is a permanent suppression requiring per-entry justification, and the reflex version of it is a blanket skip — worse than a warning nobody suppressed. Do items 1 and 2, re-measure on Linux, then decide whether the residue earns a curated list. Fixing real duplicates beats suppressing 58.
Side finding — deny.toml carries contradictory stale comments
deny.toml:173-185 has two overlapping comment blocks explaining multiple-versions = "warn". One claims "~70 distinct duplicated crate/version pairs", the other "~172 duplicate crate-versions". Actual is 58/126. Both are stale, they contradict each other, and one is clearly a leftover from an earlier edit. Reconcile to a single block with a dated number when this issue is worked.
What
cargo deny check bansemitswarning[duplicate]for 58 distinct crates / 126 lock entries. The check passes —multiple-versions = "warn"indeny.tomlis deliberate. Nothing is failing; this issue is the curated triage that comment promises ("Revisit as its own change, with the skip list justified entry by entry").Measured 2026-07-31 on
main@c2765bc, on Windows. Thewindows-sysfamily will differ on Linux CI — re-measure there before acting on bucket 2.Not caused by committing
Cargo.lockWorth writing down because it was the first guess.
cargo denyreads the lock, but duplicates live in the dependency graph — without a committed lock, CI resolves one and reports the same set. Committing it (#66) made them visible, not real, and is correct for this repo: every crate ispublish = false, socargo auditin CI now audits the exact graph that ships. Keep it committed.Bucket 1 — ours, actionable
1.
serde_jcs0.1.0 vs 0.2.0 — delete, highest valuedpp-nodedeclares it and no source file in the repo uses it. Only two lines mention it:Cargo.toml:81—serde_jcs = "0.1" # RFC 8785 JSON Canonicalisation — audit-chain hashingcrates/dpp-node/Cargo.toml:63—serde_jcs = { workspace = true }crates/dpp-types/Cargo.toml:19records that this was refactored out ("this crate no longer needs serde_jcs/sha2/hex") — the dependency was left behind. All ofdpp-coreis on 0.2 (dpp-core/Cargo.toml:64).Why this is not hygiene. JCS is the canonicalisation the signature is computed over. Two canonicalisers in one signing binary is a hazard the moment someone reaches for the wrong
use, and the failure mode is bytes we sign that core's verifier does not reproduce. It is dead today, which is exactly why removing it is free — and why leaving it is a trap with no upside.Verify before deleting:
grep -rn serde_jcs crates/ cli/should return only the two manifest lines above.2.
zip0.6.6 vs 8.6.0dpp-integratordeclareszip = "0.6"directly; its owncalamine 0.36.1pullszip 8.6.0. Bumping our direct dependency to 8.x unifies them. 0.6 is also four years stale.Check the API delta —
zip0.6 → 8.x crosses several majors, so this is a real edit, not a version-string bump.3.
image0.23.14 vs 0.24.9dpp-resolverusesimage 0.24directly;qrcode 0.12drags inimage 0.23. Not fixable by a bump —qrcode0.12 is effectively unmaintained (last release 2021). This is a "pick a maintained QR crate, or drop qrcode'simagefeature" decision, and it is the least urgent of the three. Leave it until the QR path is open for another reason.Bucket 2 — transitive, nothing to do
windows-sys×5,hashbrown×5,rand/getrandom/bitflags/base64×3, plus ~50 more at ×2. The AWS SDK, wasmtime, sqlx, axum and testcontainers each carry their own trees and do not align. Wait for upstream.Do not add a skip list yet
A skip list is a permanent suppression requiring per-entry justification, and the reflex version of it is a blanket skip — worse than a warning nobody suppressed. Do items 1 and 2, re-measure on Linux, then decide whether the residue earns a curated list. Fixing real duplicates beats suppressing 58.
Side finding —
deny.tomlcarries contradictory stale commentsdeny.toml:173-185has two overlapping comment blocks explainingmultiple-versions = "warn". One claims "~70 distinct duplicated crate/version pairs", the other "~172 duplicate crate-versions". Actual is 58/126. Both are stale, they contradict each other, and one is clearly a leftover from an earlier edit. Reconcile to a single block with a dated number when this issue is worked.