Listen to this article

TL;DR On July 30, an automated sweep drained 594.48 BTC (~$38 million) from roughly 500 single-signature Bitcoin addresses in 25 minutes. Every one traced back to a seed generated on a COLDCARD hardware wallet. The cause was not a new exploit. It was a five-and-a-half-year-old integration bug in fully open-source firmware: since v4.0.0 (March 2021), seed generation had silently consumed a deterministic software PRNG — MicroPython's Yasmarang fallback — instead of the STM32 hardware TRNG the device carried on board. I spent the last days verifying the failure myself, line by line, across the COLDCARD firmware, its crypto library, and thirteen other wallet codebases. The short version: every component worked, the composition didn't, and no amount of open source, reproducible builds, secure elements, or air gaps rescued a single key.

Disclaimer: Independent research and commentary, based on public sources and my own review of public code. Not affiliated with, sponsored by, or endorsed by any vendor mentioned; all product names belong to their respective owners. Figures are as reported by the cited sources and may be revised as the investigation continues. Not financial, legal, or security advice — verify anything that affects your funds before acting on it.

This post is long on purpose. Everything below is tagged by evidence type — what the vendor claims, what I verified myself in source at a named commit, and what is my interpretation. If you hold a seed generated on a COLDCARD 4.x device from March 2021 onward, skip to what to do about it, then come back.

594 BTC left the building in 25 minutes

In the early hours of 2026-07-30, a piece of software started spending bitcoin from private keys its operator already had. Between 01:31 and 01:56 UTC, roughly 500 transactions consumed 1,324 UTXOs and moved 594.48 BTC — about $38 million — out of about 500 single-signature addresses. The whole thing landed in blocks 960188 through 960191. Three blocks.

Every transaction carried the same fingerprint. An identical hardcoded fee of 30.0 sat/vB — a 30–75x overpayment against the 0.4–1.0 sat/vB median that week, buying near-guaranteed inclusion for a total fee bill of about 0.044 BTC. No change outputs. Exactly one transaction per address. All BIP-84 native SegWit. The uniformity is the tell: this was not 500 people independently deciding to move coins on the same night. It was a loop iterating over a key list.

Two totals circulate and both are correct within their stated scope. 594.48 BTC is the confirmed core sweep. Galaxy Research, extending the same on-chain fingerprint to 695 earlier transactions, mapped 1,196 addresses drained in full for 1,082.65 BTC (~$70.2 million) between 01:10 and 01:51 UTC. That is a scope difference, not a contradiction — and Galaxy itself cautioned that the fingerprint identifies the attacker, not the attack. Future drains of COLDCARD-generated keys don't need to match it. As of writing, the consolidation pools (562, 398, 89, and 32 BTC) haven't moved. No exchange deposits, no mixing.

The victim set is an almost perfect match to the flaw's exposure window. Every drained wallet was single-sig. Every one held more than 0.15 BTC. Many had been dormant for years — coin ages spanning 2021 to 2026, the exact interval the vulnerable firmware shipped. Atlas21's reconstruction puts the median loss at 0.41 BTC, with 110 addresses losing more than 1 BTC and the largest single loss at 29.9 BTC. No multisig and no Taproot address appeared among the 1,324 swept UTXOs — consistent with an attacker enumerating a limited set of BIP-84 single-key derivation paths rather than every script type. Jameson Lopp reported at least one victim lost only part of their UTXOs, which is what per-path enumeration looks like, and warned that a partially drained wallet is not a safe wallet: the rest can be taken in a second pass.

The July 30 COLDCARD sweep at a glance: 594.48 BTC confirmed in the core 25-minute window across roughly 500 single-signature addresses, with a hardcoded 30.0 sat/vB fee, no change outputs, one transaction per address, BIP-84 paths only — and Galaxy Research's extended attribution of 1,082.65 BTC across 1,196 addresses.

The disclosure day was brutally compressed. First public victim report on Reddit at 13:19 UTC — "Full panic – one of my wallets was drained," a 24-word seed generated on a Mk3 bought in 2021, never typed into a computer. Kevin Loaec raised the public alarm at 17:35 UTC and escalated to "This is not a drill" ninety minutes later. Coinkite's CEO initially denied any device flaw ("someone loaded a compromised seed onto a Coldcard and/or their seed leaked") — retracted within a day. A preliminary Mk3-only advisory went up that evening claiming Mk4, Q and Mk5 were "not affected based on our early analysis." Independent researchers root-caused the bug the same day and coordinated disclosure with Coinkite, publishing an engineering post-mortem before completing empirical validation because exploitation was live and waiting would have cost users money. Gregory Sanders (instagibbs) reproduced the attack on a fresh Mk3 using nothing but the number of button presses during setup: "Sorry, this is the time to panic." By 12:39 EDT on July 31 the advisory had been revised twice, scope extended to every model, hotfix firmware shipped for all of them.

Sixteen hours from first sweep transaction to preliminary advisory. Same-day independent root cause. And it still didn't matter for the victims: the core sweep needed 25 minutes, and the broader attribution window had closed 39 hours before the updated advisory landed.

There is no patch for a bad seed

Firmware bugs are not rare in this industry. Signing-logic errors, display spoofing, parsing flaws — found and patched every year, and a patched device goes back to being safe. An RNG failure in the seed-generation path is a different category of event. The defect is not in what the device does from now on. It is in every key the device has ever produced.

Coinkite's own advisory says it plainly: updating the firmware does not change or repair an existing seed. The only remedy is generating a new seed and migrating funds on-chain — a procedure that itself carries risk, since a rushed migration can create a more immediate problem than the one you're fixing. Bitcoin Optech's guidance was blunter: seeds generated on an affected device without supplemental dice rolls should be treated as compromised.

The blast radius also exceeds the device. A weak seed stays weak when you restore it onto another vendor's hardware — Trezor and BitBox both said so publicly within days. An all-COLDCARD multisig quorum inherits the weakness key by key; multisig only helps if the quorum spans genuinely independent devices. BIP85 child wallets derived from an affected seed inherit it too. And because enumeration is offline computation, the threat doesn't expire: the attacker can precompute candidate keys for years and sweep at will. Long dormancy, then a 25-minute industrial sweep — that is exactly the pattern July 30 showed.

Measured by dollars, $38–70 million doesn't rank among the largest crypto thefts; exchange and DeFi incidents routinely beat it. Measured by category, this is the largest hardware-wallet exploit and the largest weak-entropy seed theft on record. The two closest precedents bound it: Milk Sad (2023), a 32-bit-seeded Mersenne Twister in Libbitcoin's bx seed, cost about $900,000 across ~2,600 wallets. Ill Bloom (2026), weak PRNGs across several unnamed mobile wallets, drained roughly $5.7 million. COLDCARD is roughly 40–75x Milk Sad and 7–12x Ill Bloom by value, depending on which total you use — and it differs in kind: both predecessors hit software tooling, while this one struck the most security-prestigious Bitcoin hardware brand, in fully open-source firmware, after five years and four months of latency. Ill Bloom's disclosure had explicitly treated hardware wallets as the safe category. That assumption is retired.

The bug, link by link

Two public post-mortems exist: Coinkite's advisory and technical backgrounder, and an independent engineering report published the same day as the theft by an outside security team. Where they disagree I'll say so. What follows is sharpened by my own verification: I cloned Coldcard/firmware, switck/libngu, and Coldcard/micropython, checked out the exact submodule pins of the last vulnerable and first fixed tags (libngu 537519a8, micropython 4107246f — identical in both), and re-derived every mechanism claim from file-and-line evidence. The most important sharpening: both public reports frame the failure as a disabled hardware RNG plus a broken preprocessor guard. The code shows the deeper defect is a linker-level symbol collision, where the wrong rng_get() won.

What the design was supposed to be

COLDCARD was designed with layered entropy. Base layer: the STM32 microcontroller's hardware TRNG, an analog noise source producing 32-bit words, wrapped in a board driver that reads RNG->DR with a timeout fault and an adjacent-sample repeat check. Second layer: two discrete secure elements — a Microchip ATECC608A and a Maxim DS28C36 — each with its own TRNG, intended as an independent check against a compromised main MCU. A correct instantiation of this architecture fails closed in either direction: dead secure element, you still have the MCU TRNG; dead MCU TRNG, you still have secure-element entropy in the mix.

The component that was supposed to tie this together after 2021 is libngu ("Number Go Up"), Coinkite's embedded MicroPython C library wrapping Bitcoin Core's libsecp256k1. On each platform libngu defines a macro CHIP_TRNG_32() that is expected to reach hardware: esp_random() on ESP32, arc4random() on Apple/FreeBSD, and on STM32 an extern rng_get() that the build is assumed to supply from the MCU's RNG driver.

The migration that broke it

On 2021-03-01, Coinkite committed b18723dd, "First pass w/ libNgU" — a 120-file rewrite that replaced the elliptic-curve stack with libsecp256k1 and, almost incidentally, rerouted seed generation. The change in shared/seed.py was a few lines:

-from ckcc import rng_bytes
...
-    seed = bytearray(32)
-    rng_bytes(seed)
+    seed = random.bytes(32)

Before this commit, ckcc.rng_bytes filled directly from the STM32 hardware RNG. After it, seed generation flowed through libngu's CHIP_TRNG_32() — the symbol whose resolution is the entire story. I ran the git ancestor check myself: b18723dd is an ancestor of tag 2021-03-17T1724-v4.0.0, so the regression first shipped in v4.0.0 on 2021-03-17. This resolves one public discrepancy against Coinkite's advisory, which scopes the affected range from v4.0.1. The libsecp256k1 migration itself — the cryptographically sound part — is not implicated. The failure is purely in how the new library got its entropy.

This is not one bug. It's a chain of seven links, each of which survived review because each looked reasonable in isolation. Removing any one of links 1–3 would have prevented the vulnerability entirely; links 4–7 determined how bad it got.

Link 1: the board configs zero out the hardware-RNG macro. All three COLDCARD board directories define MICROPY_HW_ENABLE_RNG (0) — verified at stm32/COLDCARD/mpconfigboard.h:76 and its Mk4/Q equivalents at the last vulnerable tag. The intent was legitimate: Coinkite wrote its own RNG driver and didn't want MicroPython's built-in rng module, and zeroing the macro is the documented idiom for that. The author has since said so explicitly: "I explicitly set MICROPY_HW_ENABLE_RNG to zero, thinking we didn't need either version, but that's not what it does." What zero actually does, by value, is select a fallback software PRNG branch. The fix commit adds a confession in the same header: "LATER: when zero, this selected some PRNG code we really didnt want."

Link 2: the guard tests existence, not value. libngu's author anticipated exactly this failure mode and tried to fence it off, at ngu/random.c:22-31:

#ifdef MICROPY_PY_STM
extern uint32_t rng_get(void);
# define CHIP_TRNG_32()         rng_get()

# ifndef MICROPY_HW_ENABLE_RNG
# error "get a HW TRNG plz"
# endif
#endif

The guard is #ifndef: it fires the compile-time error only when the macro is undefined. COLDCARD defines it — as zero — so the guard passes silently. The correct guard would have been #if !MICROPY_HW_ENABLE_RNG. One token. But the guard's failure only explains why the build succeeded. It doesn't explain why the resolved function was a software PRNG. That requires the third link, which neither public post-mortem isolated.

Link 3 — the true root cause: a linker symbol collision. Two compilation units in the vulnerable build each had a relationship to rng_get. The board's own driver defined its hardware reader as static uint32_t rng_get_or_fault(void)static, so it exported no symbol at all. Meanwhile MicroPython's ports/stm32/rng.c was still in the build's object list (the board config disables the Python module, not compilation of the file), and because MICROPY_HW_ENABLE_RNG is zero, its #else branch compiled a fallback PRNG and exported a global uint32_t rng_get(void). libngu's extern rng_get() therefore bound, at link time and with zero warnings, to the only definition of that symbol in the link: the software fallback. The hardware TRNG code was present in the binary and fully functional — simply unreachable under the name libngu asked for. Coinkite's own account confirms the shape: "Existing review verified that code but did not verify end-to-end symbol resolution and call reachability from wallet seed generation."

The symbol collision that sank COLDCARD: libngu's extern rng_get() was meant to bind to the board's STM32 hardware TRNG driver, but that driver was declared static and exported no symbol — so at link time the call silently resolved to MicroPython's deterministic Yasmarang fallback PRNG, with zero warnings.

Link 4: the fallback PRNG and its seed. The function that won the link is pyb_rng_yasmarang, MicroPython's convenience PRNG for microcontrollers without a hardware RNG (verified at ports/stm32/rng.c:74-98 of the pinned fork). Yasmarang is a statistical PRNG published by Ilya Levin in 2004 — the author never claimed cryptographic security, and with a state of four words evolving through invertible arithmetic, its full state is recoverable from a handful of consecutive outputs. Its one-time seed comes from three inputs, and they are the entire entropy of the system on Mk2/Mk3: the low 32 bits of the STM32's factory unique ID (device identity, not fresh entropy, partly exposed via the USB serial number); the SysTick timer countdown register at first use (at most ~80,000 distinct values — call it 2^16.3 — and a function of boot timing); and the RTC time registers, which on legacy Mk2/Mk3 are static or zero on a cold boot because the selected RTC oscillator is disabled at startup. The seeding runs exactly once, is never re-entropied, and XORs the first two inputs into a single 32-bit word — so at most 2^32 possible results, not 2^48.

Link 5: a second Yasmarang with public constants, and a health check that certifies nothing. libngu doesn't use the chip value raw. It keeps its own Yasmarang instance — starting from the public hardcoded constants 0x0a8ce26f, 69, 233 — and XOR-mixes the two streams. XOR does not create entropy; if both inputs are reproducible, their XOR is reproducible. Whitening, not security. The adjacent-repeat check deserves special attention because it's the kind of control that gives reviewers false comfort: it rejects two identical consecutive words to catch an unclocked TRNG — but a deterministic PRNG essentially never repeats adjacent outputs, so the check passed on every boot of every vulnerable device for five years while verifying nothing about entropy.

Link 6: the Mk4/Q/Mk5 reseed keeps only 32 bits. During Mk4 development Coinkite added a boot-time reseed meant to fold secure-element entropy into the PRNG — "a backup to a backup." Verified unchanged in shared/mk4.py:39-49: it reads 32 bytes from SE1 and 8 bytes from SE2 (40 bytes of genuine secure-element output), double-SHA256s them, then unpacks only the first four digest bytes into a single uint32 and calls ngu.random.reseed(n). And libngu's reseed() overwrites exactly one state word — yasmarang_pad. The other state words stay at their public defaults, and the MicroPython fallback's state isn't touched at all. The secret state added by the reseed is therefore exactly 32 bits: at most 2^32 securely distinguished output streams. Two aggravating facts I verified in code. First, the reseed runs under a blanket try: ... except: pass — a dangerous fail-open structure — so any secure-element failure boots the device silently on public PRNG constants. Second, the Mk3 bootloader has no gate 26 at all, so on Mk3 the reseed call always raised and always silently failed; Mk3's libngu Yasmarang ran its entire life on fully public defaults. That single code fact explains the Mk3-versus-Mk4 severity split better than either public report does.

Link 7: hashing cannot repair entropy. The last link is a conceptual error, not a code defect. Seed generation reads seed = ngu.random.bytes(32) followed by assert len(set(seed)) > 4 and a double-SHA256 whitening step. None of these add entropy. The distinct-byte assert detects only a catastrophically stuck generator — any functioning PRNG trivially passes — and it predates the regression, from the era when the source was genuinely hardware. SHA256d is a deterministic public function: at most 2^32 candidate RNG outputs map to at most 2^32 candidate wallet seeds. The pipeline can only preserve or destroy entropy, never create it. Here it faithfully preserved the poverty of its input.

How weak, exactly?

The chain above converts a 256-bit security target into an attacker search space whose size depends on what the attacker knows. Neither Coinkite nor the independent post-mortem published an end-to-end brute-force benchmark, and Coinkite labels its own numbers preliminary — so these are scenarios, not point estimates.

Mk2/Mk3 on v4.x: from trivially small to about 2^40.7. The effective seed space is the number of distinguishable initial states of the MicroPython Yasmarang multiplied by uncertainty in the PRNG call history — how many RNG consumptions (approximated by user button presses during setup) happen before the 32 seed bytes are drawn. If the attacker knows your device UID, your boot timing, and your call history, the space is 2^0: fully deterministic. With a known UID but unknown timing: roughly 2^20. Lloyd Fournier's public cost model — the one Coinkite's own backgrounder links — assumes the attacker knows a ~2^20 range of device UIDs and tests ~16 button-press variants, landing at about 2^40.3, which matches Coinkite's "about 40 bits" Mk3 figure to within rounding. The generous practical ceiling, crediting the widest UID range and timer uncertainty, is about 2^40.7. Claims of "up to 57 bits" are misleading: the UID is fixed device metadata, partly exposed through the USB serial number, so crediting its full 2^32 range as attacker uncertainty is not defensible. James O'Beirne, who independently reproduced the vulnerability with AI assistance, is more pessimistic than either source — "somewhere in the 20-something bit range" for Mk2/Mk3. That dispute was still unresolved as of August 1. What is not in dispute: instagibbs reproduced the attack on a fresh Mk3 same-day, from button-press counts alone.

Mk4/Q/Mk5: a 32-bit multiplier over a weak base. The reseed multiplies the Mk3 space by at most 2^32 — the only secret bits that reach PRNG state. The independent post-mortem takes the strict position: for a fixed fallback state and call history, at most 2^32 securely distinguished streams exist, and even the loosest ceiling — treating all timer fields as uniform and independent — reaches only about 2^73.3, which "is not 73-bit cryptographic security." Coinkite's advisory claims "about 72 bits of entropy rather than the expected 128." The figures are arithmetically adjacent (40 + 32 = 72), which strongly suggests Coinkite credited the reseed as a full 2^32 multiplier on top of its Mk3 estimate and adopted the loosest ceiling. The pessimistic reading matters operationally: if the fallback state is reconstructed, the marginal cost per candidate wallet on Mk4/Q/Mk5 is a 2^32 reseed scan — large for a laptop, squarely within reach of a motivated attacker with a cluster, and permanently so, because recorded addresses never stop being derivable from the weak stream.

Effective candidate search spaces on a log2 scale: the 128-bit BIP-39 design target versus COLDCARD Mk4/Q/Mk5 at roughly 72 bits (a 32-bit reseed over a weak base), COLDCARD Mk2/Mk3 at roughly 40 bits, and Milk Sad at 32 bits — the two lower bars are within commodity-hardware reach, the spaces shrink by orders of magnitude below target.

Per-trial cost is dominated by BIP-39's 2,048-round PBKDF2-HMAC-SHA512 — Fournier amended his own laptop claim in-thread when he remembered the key stretching. My back-of-envelope estimate, labeled as an estimate: at a plausible high-end consumer-GPU throughput around 10^6 stretched derivations per second, the full 2^40 Mk3 space enumerates in days to weeks on one GPU, hours on a rented cluster of ~100, at a cost plausibly in the low thousands to tens of thousands of dollars. Against a median victim balance of 0.41 BTC and a 0.15 BTC targeting floor, the operation is profitable per recovered wallet by one to two orders of magnitude. All of that work happens offline, before any transaction exists. The monetization phase is the 25-minute broadcast burst.

Everything else the broken generator touched

Every consumer of ngu.random inherited the same entropy ceiling. I confirmed each of these call sites in the vulnerable firmware myself.

The worst case is paper wallets. COLDCARD's paper-wallet feature generates a standalone key pair whose private key is the RNG output directly (shared/paper.py:94 → libngu k1.c:430). The independent post-mortem flags why this is the most severe secondary exposure: "Unlike normal seed generation, the RNG output is used directly as the secp256k1 private key — there is no BIP39 or BIP32 step." A weak seed still requires derivation and address enumeration to match a victim; a weak paper-wallet key matches directly against the public key on-chain. The blockchain itself becomes a perfect oracle for the PRNG state.

The rest of the consumer set: Seed XOR masks (an all-COLDCARD Seed XOR split is recoverable from the same bounded stream). Cloning — COLDCARD-to-COLDCARD backup generates a per-session ECDH key pair from the same pool, so the ephemeral keys protecting cloned secrets in transit were software-derived; a passive observer of a cloning session between two vulnerable devices could in principle reconstruct the transport key. Key Teleport's 40-bit passwords, Web2FA TOTP secrets, Secure Notes passwords, HSM local-code material — all the same pool. And the multisig corollary follows mechanically: a quorum composed exclusively of vulnerable devices is only as strong as the weakest device's entropy.

One consumer appears in neither public post-mortem and I found it only in code verification: libngu randomizes its libsecp256k1 context on setup (ngu/k1.c:72-77). secp256k1_context_randomize is the library's side-channel blinding mechanism — it randomizes the internal blinding used by every subsequent signing operation, not just key generation. On vulnerable firmware this blinding seed came from the same broken pool. Signatures remain valid and this doesn't leak keys through signatures directly, but the anti-side-channel posture of every signature ever produced on a vulnerable device was weaker than designed — degrading protection against exactly the physical-observation attacks a hardware wallet exists to resist. I flag it as a code-verified omission from the public record, not as a demonstrated exploit path.

The fix is a build script

The hotfix is a single firmware commit, ca724637 ("fixes rng", 2026-07-30 22:40 -0400), touching only build files and the three board rng.c/rng.h pairs. I verified a fact with process significance: the fix changed neither the libngu nor the micropython submodule pin, and no Python file changed — seed.py, mk4.py, random.py are byte-identical between the last vulnerable and first fixed releases. Any account claiming the seed-generation code or libngu was patched is wrong. The commit does four things.

First, it fixes the collision at its root by exporting the missing symbol from the board driver: uint32_t rng_get(void) { return rng_get_or_fault(); }. Second, it excludes the upstream fallback from the build — compiling an empty object in its place and poisoning the PRNG's name with -Dpyb_rng_yasmarang=error-do-not-want-this, so any future reference fails at compile time. Third, it institutionalizes the lesson as a link-time audit: a rng-code-check target runs arm-none-eabi-nm --defined-only on both RNG objects and fails the build unless the upstream object defines no symbols and the board object defines a global rng_get. That is precisely the end-to-end symbol-resolution check whose absence Coinkite identified as the review failure. Fourth, Q1 inherited the fix for free because its rng.c is a symlink to the Mk4 one.

Two things the fix did not do deserve emphasis. The 32-bit reseed construction is unchanged at master — it still hashes 40 secure-element bytes to 4 and still sets only one state word; it's now harmless because the Yasmarang no longer determines output entropy, but the design was bypassed, not repaired. And the ineffective health checks were left in place. The fix removed the hazard rather than strengthening the alarms that had failed to announce it.

Fixed firmware shipped July 31 across every model: Mk3/Mk2 → 4.2.0 (final release for those models), Mk4/Mk5 → 5.6.0, Q → 1.5.0Q, Edge tracks → 6.6.0X/QX. One release-process flag I verified: git tags exist for v5.6.0 and v1.5.0Q, but no v4.2.0 tag exists in the public repository — the Mk3/Mk2 fix is documented only in a changelog file. For a security-critical release on the legacy models with the lowest entropy, the exact source corresponding to the shipped 4.2.0 binary cannot be checked out by tag. That weakens the reproducibility story precisely where independent verification matters most.

The structural lesson of this whole chapter: the failure was never in any single component's logic. The TRNG driver was correct. libngu's abstraction was correct on three of four platforms. The guard was present. The health checks were present. The reseed was present. The system failed at the seams — a macro tested for existence instead of value, a symbol resolved to the wrong provider, a digest truncated to one word, an exception swallowed — and every seam failure was silent. That is why five and a half years of open source, review, and even AI-assisted audit missed it: each reviewer verified that correct code existed. Nobody, until the theft forced the question, verified which code actually ran.

The forensics: a paid API account

The strongest attribution signal is not on-chain. The independent investigators who mapped the sweep stated publicly that an unusual pattern in the transactions led them to a confirmed hypothesis: the operator used a paid account at a well-known blockchain-services provider to query the source addresses and perform related activity during the sweeps. A paid, presumably identity-verified account is exactly the kind of operational-security failure that turns an anonymous on-chain actor into a name in a subpoena response. The provider wasn't named, at its own request, and the authorities have been notified. The same investigators assessed that the attacker appears to have held the information for years and then targeted dormant accounts — consistent with the long-lead precomputation the entropy math makes plausible.

What remains unknown: the operator's identity, the provider's identity, whether any law-enforcement action has followed, and which channel the attacker used to narrow the per-device search space. Two mechanisms exist, both confirmed as mechanisms and speculation as applied to this attacker. The STM32 unique ID is fixed for the lifetime of the chip and partly transformed into COLDCARD's USB serial number, so any channel exposing serials — reseller records, support tickets, warranty databases, photographs — shrinks the UID term. And testing an attacker-owned device under controlled boot conditions lets you profile the timer and call-history distributions, narrowing candidate states for every other device of the same model. The instagibbs reproduction suggests button-press call history alone may have sufficed on typical devices. No public attribution from the usual independent investigators existed as of August 1.

There's also an AI-assisted discovery theory. Coinkite's CEO assumes — his word, no evidence — that someone used AI to review previous firmware versions and stumbled on the issue. What is confirmed: Coinkite ran one of the best available frontier models over its own codebase weeks before the theft and it found nothing serious, while multiple independent developers reproduced the vulnerability within hours of disclosure using AI assistance. A Russian tabloid's claim that a specialized agent found the bug in under an hour is single-source and unverified; I assign it no weight. How the first attacker got there is indeterminate from public evidence. Why it matters anyway: Bitcoin Optech's conclusion — because several developers could immediately reproduce the attack with frontier AI models, every vulnerable seed should be assumed under active exploitation regardless.

The vendor response was its own incident

Coinkite's public posture moved through four phases in about 36 hours. Denial at 18:10 UTC on July 30 ("someone loaded a compromised seed onto a Coldcard and/or their seed leaked"). A preliminary Mk3-only advisory that evening declaring Mk4, Q and Mk5 "not affected based on our early analysis." Two revisions the next morning extending scope to Mk4, Mk5 and Q seeds generated before the fix. Finally an unreserved apology from the CEO — "I'm sorry and I'm devastated" — and full accountability for the firmware bug.

Beneath the evolution sit four substantive disagreements with the independent post-mortem, and my code verification resolves most of them:

Question Independent post-mortem Coinkite Code-verified resolution
First vulnerable Mk3 firmware v4.0.0 (2021-03-17) v4.0.1 onward Independent report correct: b18723dd is an ancestor of the v4.0.0 tag
Mk2 in scope Confirmed vulnerable, same as Mk3 Advisory silent Same board directory, same firmware family; the 4.2.0 binary is labeled for Mk3 and Mk2 — include Mk2
Mk3 effective entropy Deterministic for known state "~40 bits" (preliminary) Compatible: one states the mechanism, the other the assumed-knowledge estimate; Fournier's decomposition lands at ≈2^40
Mk4/Q/Mk5 entropy ≤2^32 secure streams; loose ceiling ≈2^73.3 "~72 bits" Partially reconciled: 72 ≈ 40 + 32; Coinkite used the loose ceiling. Vendor optimistic, independent analysis pessimistic
Mk4/Q/Mk5 affected at all Affected from the start "Not affected" → reversed within ~24h Affected: reseed first shipped v5.0.1; Mk3 bootloader lacks the reseed gate entirely

The row that matters most for users is the second one. Coinkite's advisory never mentions the Mk2, yet the Mk2 runs the same board firmware as the Mk3, and the final fix release is explicitly labeled for both models. Mk2 owners reading the vendor's advisory received no warning at all, while independent researchers treated Mk2 as affected from day one. The severity gap in row four is best read as a methodological difference: one side counts only key-dependent secret state (32 bits), the other counts total assumed attacker uncertainty (~72 bits). Both describe the same code; they answer different questions, and the security-relevant one is the strict count, because attacker knowledge of device state must be assumed to grow, not shrink, over time.

Why did reviews miss it for five and a half years? Coinkite's backgrounder is, by vendor-postmortem standards, unusually candid. Signature collision: the intended driver and the unwanted fallback exported the same function signature, so the build completed without identifying the wrong implementation. Presence versus reachability: reviewers confirmed the careful TRNG code was present in the binary, but never verified end-to-end symbol resolution from seed generation. The broken #ifndef guard. And the CEO's personal admission: "The bulk of randomness on the COLDCARD was coming from a PRNG that I didn't know was actually in the source code base… the carefully crafted TRNG code I wrote was being used, but just by chance, and only for less important things." Add the two compounding failures — health checks structurally incapable of catching a deterministic substitute, and the frontier-model audit weeks before the theft that found nothing — and you have the complete anatomy of the blind spot.

The institutional aftermath was thinner than the technical one. As of August 1: no CVE assigned, no CERT or government advisory, coordination entirely vendor- and researcher-driven. A plaintiff-side law firm published a victim-intake page within 48 hours. Coinkite pledged to assist with police reports and insurance claims but announced no compensation fund. Critics also resurfaced Coinkite's 2021 handling of an external multisig disclosure — shipped without a security-critical label, bounty request ignored — as evidence the disclosure-culture problem predates this incident.

This bug is eighteen years old

COLDCARD is not a novel bug. It is the firmware incarnation of a class that has recurred across web, mobile, library, and operating-system ecosystems for two decades. Every incident shares one structural signature: a component that was supposed to supply TRNG- or CSPRNG-grade entropy silently didn't, no error was surfaced, and the weakness was discovered only after weak keys met the public blockchain.

The ur-precedent is Debian OpenSSL (CVE-2008-0166): in 2006 a maintainer removed two lines from OpenSSL's seeding code chasing a Valgrind warning, leaving the process ID as effectively the only seed material — at most 32,767 distinct key streams. Every SSH, TLS, and VPN key generated on Debian-derived systems for twenty months became enumerable, including early Bitcoin keys. Found by code review, not by any runtime alarm — there was none.

Then the canonical software cases. Android SecureRandom (2013): the platform PRNG failed to self-seed, repeated ECDSA nonces leaked keys on-chain, ~55.82 BTC reported stolen — a signing-time branch of the family, largely retired later by RFC 6979 deterministic nonces. Blockchain.info, twice: a 2014 update that emptied an entropy array without raising an exception (~250 BTC at risk, ~870 BTC protectively swept by a white-hat), and the 2015 Android app that fetched entropy from random.org over unencrypted HTTP — when random.org started requiring HTTPS, the fetch silently failed and the app generated the same private key for every affected user, sending ~34 BTC to one stranger's address. That 2015 incident is the purest documented instance of the COLDCARD signature: the source died, the software noticed nothing, and key generation proceeded on a constant.

Milk Sad (CVE-2023-39910) is the closest precedent. Libbitcoin's bx seed seeded a Mersenne Twister with a 32-bit value from the system clock — about 4.29 billion candidate seeds regardless of requested length. The vulnerable code states its own indictment: // Use the clock for seeding. The name comes from the first two words of the BIP39 phrase the generator emits at timestamp 0. Over $900,000 confirmed stolen; discovery came from victims who had generated seeds air-gapped and self-compiled and were drained simultaneously. The same 32-bit time-seeded key space was later attributed to the LuBian mining-pool compromise — ~127,426 BTC, roughly $3.5 billion at the time — which would make this class the vector behind the largest known weak-RNG theft, full stop.

The rest of the canon: Randstorm (JSBN's SecureRandom() silently skipped its entropy-pool step and fell through to weak browser Math.random(); ~1.4M BTC sits in potentially weak 2011–2015 wallets — an exposure estimate, not confirmed theft). Trust Wallet twice — a 32-bit-seeded MT19937 in the WASM build (CVE-2023-31290), and an iOS build that reused trezor-crypto's test-only random32()srand(time(NULL)) plus libc rand() — for BIP39 generation (CVE-2024-23660); two users generating wallets in the same second got the same seed. Profanity seeded its vanity-address search with 32 bits; Wintermute lost $160 million when the key of its admin address was recovered — after the public warning, because they removed the ETH but never revoked the address's admin role. And Ill Bloom, disclosed three weeks before the COLDCARD sweep: insecure PRNGs in mobile wallets, code reused across projects, over $5 million confirmed drained, "a floor, not a ceiling."

Two decades of entropy failures on a log scale, from Debian OpenSSL in 2008 through Android SecureRandom, Blockchain.info, Milk Sad, Randstorm, Trust Wallet, Profanity/Wintermute, and Ill Bloom to the COLDCARD sweep in 2026 — losses spanning five orders of magnitude, with 32 bits recurring as the fatal quantum and every case discovered by key-space reconstruction after theft rather than by any runtime health check.

Three observations fall out of the table. First, the losses span five orders of magnitude — $8,100 to $3.5 billion — yet the underlying defects are often a single edit: two removed seeding lines, one 32-bit seed, one unchecked network failure. Second, 32 bits recurs as the fatal quantum: Milk Sad, Trust Wallet, Profanity, and the residual COLDCARD reseed state all collapse to a ≤2^32 search space, enumerable on commodity hardware. Any 32-bit value in an entropy path is a red-flag invariant. Third, the detection column is uniform: no case was caught by runtime testing or a shipped health check. Each surfaced through weak-key-space reconstruction after theft, post-hoc code reading, or victims comparing notes. COLDCARD fits exactly — its adjacent-repeat health check passed Yasmarang output trivially for five years.

The recurring structural pattern across all of it: an insecure-by-default library primitive is silently inherited by an integrator. MicroPython shipped a Yasmarang fallback. trezor-crypto shipped a time-seeded random32() marked test-only. JSBN shipped a SecureRandom() that skipped entropy collection without an exception. Libbitcoin shipped bx seed. In each case the weak default works — it returns bytes, passes smoke tests, raises nothing. The integrator's catastrophe is inherited, not written. Two corollaries. Entropy is a conserved quantity: no downstream transformation — SHA256d, BIP39 checksums, XOR with a second deterministic stream — repairs an upstream truncation; the effective security of a seed is the minimum over the chain, not the maximum. And detection methodology has not evolved with the bug class: discovery still means reconstructing the weak key space and matching it on-chain, after the money is gone.

Audited nine hardware wallets

The question the disclosure immediately raised: which other wallets could carry the same defect? I audited nine hardware families against six failure modes abstracted from the COLDCARD root cause — silent fallback, ambiguous library RNG, entropy truncation, fail-open boot, PRNG-passable health checks, and single-source-without-mixing. Code-verified means I confirmed it against a local clone at a named commit: Trezor, BitBox02 (c838d7f), Passport, Jade, SeedSigner, Keystone (354369b), OneKey (e99ba63). For Ledger the evidence is certification documents — the ANSSI CSPN security targets and lab reports, which included source access under NDA. For Tangem, published audit summaries. Unverifiable is a finding, not an omission.

The headline: no other audited wallet shares the bug. And the decisive counter-example runs the same software stack.

Foundation Passport is the controlled experiment that eliminates every confounding variable except engineering discipline. Same MicroPython-on-STM32 platform, same vendor HAL, the identical Yasmarang fallback present at the same path in its tree — its TRNG wrapper even derives from COLDCARD's 2018 code, copyright header intact. If platform or pedigree determined outcomes, Passport would be the most exposed device on the market after COLDCARD itself. It is instead among the least exposed, for three reasons visible in code. Its board config sets MICROPY_HW_ENABLE_RNG (1) with a value-based guard, so the Yasmarang fallback is compiled out of the production binary — not configured out, compiled out; no linker accident can resurrect code that isn't in the image. It never had COLDCARD's libngu routing layer, so there's no integration seam where a symbol collision could live. And seed generation bypasses MicroPython's random module entirely, calling a dedicated three-source mixer that XOR-folds an avalanche noise circuit, the MCU TRNG, and the ATECC608A, then SHA-256 compresses — with fail-closed checks that halt on 0, ~0, or duplicate samples, the direct opposite of COLDCARD's len(set(seed)) > 4 theater. My audit did surface one latent flag worth public record: new_seed_task.py:13 ignores the boolean return of random_bytes(), fail-open only under a hardware-fault assumption, low-severity because two further sources mix into the same buffer — but an assert costs one line.

BitBox02 is the canonical counter-example — the cleanest existing answer to "what should COLDCARD have done?" Five independent sources feed every seed, verified at src/random.c:72-92 and keystore.rs:573-606: the MCU TRNG, the secure-chip RNG, 32 bytes of per-device factory randomness, host entropy from the BitBoxApp, and a salted hash of the device password. The documented design goal is precisely the anti-COLDCARD property: "at least as strong as the strongest of all, not the weakest of all," because XOR of independent sources can only add entropy. On error it fails closed — secure-chip RNG failure maps to an abort, a dead MCU TRNG hangs rather than degrading silently. The only software fallback in the tree is a rand() path behind #ifdef TESTING, defined solely for host unit-test builds — no macro-value ambiguity of the COLDCARD kind, and reproducible builds let anyone confirm the shipped binary took the hardware path.

Trezor — safe today, with the same scar in its own history. Current firmware XORs the MCU TRNG with the Optiga secure element (plus TROPIC01 on Safe 7) in 32-byte blocks, returns false on any SE RNG error, and hashes device entropy together with host-supplied external entropy. The instructive part is historical: Trezor's tree contains exactly one instance of the silent-fallback mode, and it's the same bug shape as COLDCARD's. Before April 2023, an inverted opt-out guard meant that forgetting a define silently produced a zero-seeded deterministic LCG — and Cure53's pentest found the Model T bootloader was built without the flag, so its anti-fault-injection random delays were deterministic. The fix inverted the macro to opt-in. The blast radius was random delays, not seeds — but the topology (opt-out guard, forgotten define, silent deterministic fallback) is COLDCARD's, three years earlier and one layer luckier. Trezor's ecosystem also supplied the canonical library-ambiguity case: trezor-crypto's test-only random32() is the function Trust Wallet iOS shipped to production. Trezor devices were never affected — the trap caught a downstream integrator instead. Residual: Model One and Model T have a single silicon entropy source, mitigated by host-entropy mixing and an entropy-check protocol added in core 2.8.7.

Blockstream Jade answers with architectural maximalism: no seed byte ever comes from a single source. All randomness flows through one choke point feeding a SHA-512 entropy pool — six power-rail sensor readings, die temperature, cycle counter, uninitialized stack, RF/bootloader TRNG output, rolling pool state, a monotonic counter — the Bitcoin Core pool pattern. This matters because Jade's ESP32 platform carries a documented conditional-TRNG hazard, and the mitigation is explicit in code: bootloader_random_enable() at entry, 32 true-entropy bytes drawn, then the window closed, plus a one-second iterated-SHA-512 strengthening loop and camera frames refed into the pool. Even if all esp_fill_random output were degenerate, the sensor, jitter, and stack terms keep the pool non-deterministic.

Keystone 3 mixes three hardware sources from three different silicon vendors — MHSCPU MCU TRNG, Maxim DS28S60, Microchip ATECC608B — plus the password hash, through an iterated HKDF chain, with asserts on every error path. The strongest multi-vendor construction in the open-source set.

SeedSigner resolves the bug class by abstention. A recursive grep finds no os.urandom, no secrets, no /dev/hwrng anywhere in the application. Seeds come from user dice rolls, coin flips, or a camera-entropy chain. The Pi kernel config even disables the hardware RNG driver — the platform's RNG is simply outside the trust path. The verdict is structural: there is no TRNG to fall back from, no library to be ambiguous, no health check to lie.

Ledger eliminates the specific bug mechanism architecturally — dual-chip, the secure element runs onboarding, no MicroPython, no third-party crypto library, no link-time seam — and carries the strongest closed-design assurance available: ANSSI CSPN evaluations that name the TRNG as Security Function #1 against the explicit threat of "a biased or a deterministic random number," AIS-31 PTG.2 certification of the noise source (which mandates online statistical tests of the physical source itself — exactly the property COLDCARD's output-side assert lacked), and a dedicated RNG analysis by an accredited lab with source access, concluding no non-conformity, for both Nano X and Stax. The residual risk is irreducible: the documented design is single-source — one SE TRNG plus post-processing, and post-processing conditions but cannot add independent entropy. No user entropy path exists. Whether BOLOS contains any internal fallback, and whether evaluated firmware versions match what ships today, is unverifiable from outside. The verdict is delegated to ANSSI's labs rather than demonstrated.

Tangem is the limiting case: one Samsung secure element, key generated inside the chip by its TRNG, key never leaves the chip, firmware immutable. Against single-source-without-mixing it fails by design — mixing is impossible because mixing requires seeing the key material. Everything else is unverifiable: closed firmware, unpublished full audits, claimed AIS-31 evidence not publicly inspectable. Tangem's own post-incident statement concedes the epistemic point: "A key made from 40 bits of entropy looks identical to a key made from 256 bits… This is why entropy defects can survive in shipped products for years." Applied to Tangem itself, that statement is exact. A COLDCARD-class defect inside this card would be COLDCARD-fatal and, unlike COLDCARD, could never be discovered from source.

OneKey is a Trezor fork, and the audit shows both the value and the fragility of inherited soundness. The Trezor seed construction is intact and fail-closed. But the secure element's RNG is never mixed into seeds — it serves storage encryption and nonces, so a second silicon source sits on the board unused by the seed path (which contradicts OneKey's own Milk Sad-era marketing about its chip's TRNG). And the tree still carries the trezor-crypto LCG trap in its original pre-fix form: an opt-out guard of exactly the shape that produced Trezor's #2828 and COLDCARD's catastrophe, protected only by a #pragma message — a compiler warning, not a link error. Both production build systems define the macro today, so it's compiled out. But a forgotten define away from failure mode one.

Entropy sources in the seed path per wallet family, code-verified: Blockstream Jade mixes at least six sources, BitBox02 five, Keystone and Passport three each, Trezor two to three, while Ledger, Tangem and OneKey rely on a single source — and pre-hotfix COLDCARD carried two healthy hardware sources on the board but consumed neither in the seed path.

Read the matrix as a whole and three conclusions hold. No audited wallet besides COLDCARD carries the exploitable bug today. The residual-risk population is defined by the single-source column — Model One/T, OneKey, Ledger, Tangem each survive only while one source stays healthy, which is the precise structural position COLDCARD was in, differing only in how well that source is tested and who is allowed to look. And evidence type is itself a risk dimension: the open wallets' verdicts are reproducible by any reader at the cited commits, Ledger's are delegated to accredited labs at point-in-time firmware versions, and Tangem's internals admit no verdict at all. The dividing line is not open versus closed source — COLDCARD was open and fell, Ledger is closed and stands. It is multi-source mixing verified at the consumption point, fail-closed behavior on source error, and weak paths compiled out of production binaries.

Audited four software wallets

Software wallets sit in a structurally different position: none own an entropy source, all delegate to the host OS CSPRNG. The audit question splits in two — could the wallet's own code reproduce a COLDCARD-analogous failure, and does the delegation itself reintroduce platform risk. I checked Bitcoin Core, Electrum, Sparrow, and BlueWallet against current master clones.

Bitcoin Core runs the strongest entropy stack audited, and its git history reads as a decade-long elimination of every COLDCARD-class failure mode. Every OS-entropy error path terminates in RandFailure(), which logs and calls std::abort() — there is no downgrade branch. GetStrongRandBytes() mixes fresh OS entropy with hardware RdRand/RdSeed, stack pointers, performance counters, and carried-over state, so no single CSPRNG read is ever the sole basis of a key. Unique among the wallets audited: Random_SanityCheck() runs at every startup and verifies the OS entropy hook actually overwrites all 32 output bytes — detecting a misintegrated entropy source that silently returns zeros, the exact misintegration signature of the COLDCARD bug. A 2014 commit introduced abort-on-failure after OpenSSL's RAND_bytes failure was caught only by an assert that vanished in release builds; a 2016 commit mandated OS randomness for secret keys; the OpenSSL dependency was removed entirely in 2018. Even a fully broken platform CSPRNG yields an abort, not a fallback.

Electrum takes the opposite bet: no mixing, no self-test, pure reliance on the OS through Python's secrets module. The critical property is what's absent: secrets has no fallback path — if os.urandom fails, Python raises, and the wallet cannot generate a seed rather than generating a weak one. No truncation; the effective floor stays at or above 128 bits. But entirely single-source: Electrum cannot detect a deterministically seeded kernel pool — early-boot embedded Linux, a restored VM snapshot — because it performs no startup self-test.

Sparrow has the one audited site where a catch block substitutes a weaker RNG: SecureRandom.getInstanceStrong() falling back to new SecureRandom(). It superficially resembles the COLDCARD pattern; it differs in kind. The fallback is still seeded from the OS and throws if that source is unusable — it downgrades a provider guarantee, not the entropy source. No truncation. The residual risk is the delegation problem in JVM form: an operator who points java.security.egd at a weak provider silently degrades every consumer, with no in-app detection.

BlueWallet is the post-Randstorm remediation pattern executed correctly. JavaScript runtimes ship no CSPRNG at all, and the classic React Native wallet bug class is generating keys from whatever shim happens to resolve. BlueWallet imports react-native-get-random-values before anything else — bridging to SecRandomCopyBytes on iOS and java.security.SecureRandom on Android, throwing if the native module is missing — and every wallet-secret path funnels through one audited choke point with an explicit availability check and no Math.random fallback. It never calls bip39's bundled RNG. The boundary is thin — one import line and one native bridge — but failures are loud, not silent.

The structural finding across all four: none contains a COLDCARD-analogous code path — all fail closed where COLDCARD failed open — yet three of the four achieve this by inheriting platform CSPRNG health rather than verifying it. Fail-closed protects against a CSPRNG that reports an error. It says nothing about a CSPRNG that returns confidently wrong output. The historical record shows where that inherited trust has failed: early-boot Linux entropy, VM snapshot restores duplicating RNG state, Android 4.x, pre-WebCrypto browsers, and one layer down entirely — Debian OpenSSL gutting the entropy every application above it consumed for twenty months. None of these conditions exists on a healthy modern system. Each is a platform condition, invisible to wallet code that cannot test the entropy it consumes. Only Bitcoin Core even tries.

What actually separates safe from sorry

Five structural insights fall out of the incident record, the two-decade taxonomy, and the thirteen-wallet audit.

Integration seams, not components. Every individual component of the COLDCARD entropy chain performed as designed. The catastrophic failure was a property of the composition: a guard that tested existence instead of value, and a linker that silently bound the extern to the wrong provider. Neither defect is visible by reading any single file. This is the firmware incarnation of the most reliable entropy bug pattern of the past two decades — a dependency ships a weak default that compiles cleanly, fails silently, and transfers its risk to every integrator downstream. The review methodology this implies is link-time and runtime, not source-time: which concrete symbol does each security-critical call resolve to, and what actually executes. Coinkite's own fix adopted exactly that posture.

Entropy is conserved. For any deterministic function, H(f(X)) ≤ H(X). Hashing whitens; it never adds. The security of an entropy chain equals the minimum over every transformation in it, not the maximum over its sources. The Mk4 path is the cleanest demonstration on record: 40 bytes of genuinely good secure-element entropy, hashed, truncated to 4 retained bytes, reseeded into a single 32-bit state word — 256 bits of source entropy capped at 2^32 distinguishable streams. The same law doomed Milk Sad, Profanity, and Trust Wallet. In every case wide output formatting concealed narrow state. And this is why output-side health checks are detection theater: producing statistically plausible output is what a PRNG is for. The control that binds is at the source — statistical validation of the physical TRNG against a stochastic model, AIS-31 PTG.2 style — plus runtime proof that the tested source is genuinely in the seed path. COLDCARD proves even a perfectly validated source is worthless if the consumption path never reaches it.

Mixing is the empirically validated defense. The audit splits cleanly on one property: whether independent entropy sources are combined at the point where seed material is consumed. Every wallet that mixes two or more independent sources is structurally resilient against any single-source failure — a broken source degrades into a no-op, not a vulnerability. The dividing line is none of the properties the industry markets. Not open versus closed: Passport and COLDCARD run the same stack with the identical fallback in both trees and land at opposite ends. Not the presence of good hardware: COLDCARD carried two healthy hardware entropy sources and its seed path consumed neither. What a board carries is not what a seed consumes. Mixing must be verified at the consumption point — in the call graph of the function that emits the seed — never inferred from a datasheet's capability list.

"Many eyes" failed; AI changed the economics on both sides. The regression sat for five and a half years in one of the most scrutinized open-source codebases in Bitcoin — reproducibly built, so anyone could confirm the shipped binary matched the source — and was found by no one. The failure has four layers: the vulnerable code lived in a boring submodule reviewers skim; prior review verified code presence, never reachability; the ecosystem's verification energy went into reproducible builds, a control logically incapable of catching a source-level design error; and the triggering defect is a one-token C pitfall human reviewers pattern-match past. TFTC's synthesis enumerates the stack failing independently: open source did not stop the bug, reproducible builds did not stop the bug, secure elements did not stop the bug, an air gap did not stop the bug — each protects against a class of failure, and none can rescue a private key created with inadequate entropy. Openness is a potential for review, never its actualization. Then the inflection: Coinkite's own frontier-model audit missed the bug weeks before the theft; independent developers reproduced it with AI assistance within hours of disclosure. AI collapses the cost of deep cross-module auditing — exactly the analysis the many-eyes hypothesis always assumed but never funded — for attackers and defenders simultaneously. Any bug an AI can find must be assumed found. The window between "latent in public source" and "actively exploited" is closing toward zero.

Seed-level bugs are unforgiving. A signing bug can be patched; an entropy bug is baked into every key already generated. Remediation is migration, not a patch — every affected user moving funds on-chain, under time pressure, on the very device class whose trustworthiness is in question. The weakness propagates along every derivation and export edge: restored into other vendors' devices, into BIP85 children, into all-COLDCARD multisig quorums. And the long tail may never be reached: vulnerable seeds date to 2021, the Mk3 has been out of support since 2023, and Coinkite's 120-day customer-record purge — an anti-surveillance feature — now guarantees most dormant holders cannot be notified. Seed provenance has become a first-class security property that outlives the hardware. Compounding it is a cost asymmetry that makes disclosure timing itself a security event: the attacker's work is one-time, offline, parallelizable precomputation, after which sweeping is a 25-minute broadcast — so once the bug class is public, every unhealed address is a standing bounty, indefinitely.

What to do about it

If you own a COLDCARD. The exposure test is provenance: which firmware generated the seed, on which model. The two technical sources never reconciled the range start, so the conservative rule treats any seed generated on a COLDCARD 4.x device from March 2021 onward as in scope. Mk2 users should follow the Mk3 guidance — the vendor's advisory omits the Mk2, but the code and every independent analysis place it on the same vulnerable path. The migration playbook: install the fixed firmware (Mk3/Mk2 4.2.0, Mk4/Mk5 5.6.0, Q 1.5.0Q, Edge 6.6.0X/QX — note that a higher Edge version number does not by itself imply the fix), generate a new seed, move funds with a small test transaction first, confirm, then move the remainder, and keep the old backup until migration is confirmed. Updating firmware does not repair an existing seed. Rushing the migration can be a bigger risk than the bug.

Three refinement rules. Seeds built from 50 or more independent, unrecorded dice rolls carry at least 128 bits of user entropy and are not considered at risk from this bug — the dice-only import path on 4.2.0 bypasses the device RNG entirely. A strong, unique BIP39 passphrase is an independent barrier that buys time but is not grounds to skip migration — Coinkite itself walked back its early "minimal risk" phrasing. And a partially drained wallet is not a safe wallet: remaining funds can be taken in a second pass over a wider derivation-path set.

Two structural defenses survive any vendor's seed compromise. Multisig across vendors — a quorum of exclusively vulnerable devices stays vulnerable, but a 2-of-3 spanning three manufacturers loses redundancy rather than funds when one key class breaks; the empirical record agrees, since no multisig address appeared among the 1,324 swept UTXOs. And seed provenance discipline: a weak seed stays weak forever, importing it into another vendor's device changes nothing, so record which device and firmware produced every seed you hold. Finally: anyone offering to "recover" your coins for a fee is running the secondary scam that follows every incident.

If you build wallets. Three controls, each hours of engineering, each of which would have caught this defect on introduction. Make insecure PRNGs opt-in for test builds only — Trezor's inverted guard, BitBox02's #ifdef TESTING scoping — so a missing platform RNG is a link error, never a silent fallback. Add a link-time symbol audit to CI — Coinkite's hotfix is the reference implementation: fail the build unless the intended entropy symbol is defined by the intended object and every known fallback symbol is absent; where a fallback must exist for tests, poison its name so accidental resolution is loud. And assert the call chain from each key-generation entry point to its entropy source — the right question is not "is the good code in the binary?" but "which code does random.bytes(32) actually reach?" On top of that: mix at least two independent sources at the consumption point, publish an end-to-end entropy budget per release scored by the minimum over the chain, fail closed on any source error, and test the source, not the output stream. Check every return value on the entropy path — even exemplary mixers carry fail-open seams at the call site.

If you audit wallets. Treat Coinkite's own post-mortem sentence as a scope-change mandate: "Existing review verified that code but did not verify end-to-end symbol resolution and call reachability from wallet seed generation." Verify symbol resolution and runtime reachability across submodule boundaries — an audit that stops at repository boundaries, or confirms only that a TRNG driver exists in the binary, is incomplete. Inventory every dependency's weak-but-compilable RNG default and its resolution fate; the recurring systemic vector is the insecure-by-default library primitive. Run fork-diff audits at entropy consumption points — OneKey inherited Trezor's sound architecture and silently narrowed it, so "fork of a safe codebase" is a finding generator, not an assurance. Demand entropy budgets in design documents as a review precondition. And assume asymmetry: treat any AI-findable vulnerability as under active exploitation from the moment of discovery, because the reproduction cost has collapsed.

For the ecosystem. Three institutional gaps. No CVE or government advisory existed for this incident as of August 1 — coordination ran through vendor blogs and social media; the effective response was improvisation, not process, and the ecosystem needs standing entropy-incident playbooks written before they're needed. Coinspect's Ill Bloom disclosure paired its advisory with a public per-address risk checker; equivalent tooling should accompany every future entropy incident so triage doesn't depend on reading firmware changelogs. And no hardware wallet holds a FIPS 140 validation for its wallet product; only Ledger carries accredited RNG-specific evaluation, which proves it's feasible — the COLDCARD bug class, integration-layer misrouting, is precisely what a consumption-point-aware evaluation criterion would target, and current schemes don't test it.

The next entropy failure is already compiled into something shipping today. The defensive economics, though, have shifted for the first time: the same AI-assisted analysis that plausibly found this bug for an attacker makes cross-module, symbol-resolution auditing cheap enough to run on every release, and the reference implementations — Coinkite's link-time symbol check, Trezor's opt-in guard, BitBox02's five-source mixer, Bitcoin Core's startup sanity check, Coinspect's public address checker — are public and proven. What remains is the unglamorous work the ecosystem skipped for five and a half years: tracing every seed to its physical bits, at the consumption point, on real hardware, before users' savings depend on the answer. The industry that learns to verify entropy at birth rather than audit it at autopsy will not need another July 30.