Fail-closed BIP39 seed generation from a Geiger counter on a Raspberry Pi, with an independent OS CSPRNG XOR floor.
Use at your own risk. This is not an externally audited product and is not claimed suitable for meaningful funds without independent review.
Bitcoin security starts at seed generation. If the entropy that produces a BIP39 mnemonic is weak or predictable, later air-gapping, hardware signing, and careful backup hygiene cannot recover that mistake.
RadioSeed exists for two related reasons — one from 2021, one from 2026.
The project was initially motivated by @raw_avocado arguing that the entropy behind a Bitcoin seed is what makes a coin yours, that common DIY rituals (dice, coin flips) are still deterministic physical processes that are merely hard to measure, and that radioactive decay is one of the few everyday processes that is random in the quantum-mechanical sense. The practical recipe in that thread — detect decays with a Geiger tube, treat the unpredictable time between clicks as the entropy source, and turn that into a 24-word seed on a Raspberry Pi — is exactly what RadioSeed set out to automate on an air-gapped box.
An early demo of this repo in operation is here: https://twitter.com/Arceris_btc/status/1435692937153744898
Hardware notes from that original design still apply: a Geiger counter that exposes a pulse output (MightyOhm Geiger Counter Kit), Pi-powered display and tube, pulse on physical pin 12 / GPIO18, then disconnect the Pi from the network before generating anything you care about. The first implementation was adapted from Apollo-NG / PiGI.
This hardening rewrite is an audit and upgrade prompted by public Coldcard seed-generation failures. Coinkite’s own account is that wallet seed generation could silently resolve to a MicroPython software PRNG fallback instead of the intended hardware RNG — while the TRNG code was still present in the binary and used for less important paths. A device can stay offline, look healthy, and still produce a weak seed if the end-to-end generation path is wrong.
After reports of thefts and active exploitation associated with weak seed
generation, more people may reach for DIY / external-entropy generators. That
made cleaning up RadioSeed urgent: the old code had its own Coldcard-class
failure mode — when RPi.GPIO was missing it fell open to
random.random()-timed simulated ticks, printed a brief “Simulating” warning,
then clear erased that warning before the mnemonic appeared.
The architectural lesson, stated carefully: a source can exist, be reviewed,
and appear operational while the actual seed path resolves elsewhere. Coinkite’s
hotfix adds a build-time rng_get() symbol check; RadioSeed’s answers are
fail-closed live GPIO, continuous health tests, modern packaging (uv + Typer),
and an independent OS CSPRNG XOR floor so one honest source can still carry the
seed if the other fails accidentally.
Dice / user-entropy mixing on Coldcard is the mirror image of that XOR floor: seeds created with enough independent private dice rolls were treated as not at risk from the device RNG bug alone, because two independent sources were combined. That only holds when the failing source cannot adaptively observe or choose against the survivor — the accidental-integration threat class addressed here, not a malicious OS.
Origin
- Motivation thread (@raw_avocado): https://twitter.com/raw_avocado/status/1433408813596545027
- Early RadioSeed demo (@Arceris_btc): https://twitter.com/Arceris_btc/status/1435692937153744898
2026 Coldcard entropy issue
- Coinkite advisory thread: https://x.com/COLDCARDwallet/status/2082961993070247948
- Coinkite technical backgrounder: https://blog.coinkite.com/entropy-technical-backgrounder/
- Coinkite Mk3 advisory / migration guidance: see current posts on https://blog.coinkite.com/ (open-ended “Mk3 running firmware 4.0.1 or later”; fixed versions for other models are published there)
- Block Engineering analysis: https://engineering.block.xyz/blog/predictable-rng-fallback-and-32-bit-reseed-in-coldcard-firmware
See also SECURITY.md and docs/entropy-design.md.
- Radio required + health checks; then
SHA-256(radio) XOR secrets.token_bytes(32)(+ final domain hash). - Does not protect against a compromised OS/runtime/display stack.
- Health checks ≠ proof of 256-bit radio min-entropy.
- Supported for
seedin v1: Raspberry Pi 4 and Pi 5, 64-bit Raspberry Pi OS, Python 3.11. - GPIO18 = physical header pin 12. MightyOhm J6 pulse is active-high (~100 µs).
Prefer the hash-verified Linux wheelhouse procedure in the docs (courier must be Linux, not macOS/Windows, for reliable foreign-platform downloads):
- On a Linux courier:
uv export --frozen --no-emit-project --no-dev -o requirements.txt - Build an embit wheel (
pip wheel embit) and append its sha256 as a second hash pip download … --platform manylinux2014_aarch64 --python-version 311 --implementation cp --abi cp311 --only-binary=:all:- Sneakernet wheels + reviewed source to the Pi
- On the Pi:
uv venv --python /usr/bin/python3.11 --no-python-downloads uv pip install --no-index --find-links wheels --require-hashes -r requirements.txt uv pip install --no-index --find-links wheels --no-deps radioseed==1.0.0
Dev (networked):
uv sync --group dev
uv run radioseed --versionNever apt install python3-libgpiod on Bookworm for this project (v1 API).
User should be in the gpio group — do not run seed as root.
# Preflight
uv run radioseed status --seconds 10
uv run radioseed profiles
# Generate (interactive TTY; local console — not tmux/SSH)
sudo dphys-swapfile swapoff # or equivalent; seed refuses disk swap by default
uv run radioseed seed --profile mightyohm-check-source --no-passphrase
# Optional dangerous mnemonic file (typed confirmation: WRITE MNEMONIC)
uv run radioseed seed --profile mightyohm-check-source --no-passphrase \
--write-mnemonic ./wallet-mnemonic.txt
# Analysis exports only — NOT wallet seeds
uv run radioseed entropy bits --simulate --format hex
uv run radioseed entropy intervals --simulate --count 50--radio-bits defaults to 1024 (multiple of 1024). With the default
--extractor interval-pair (~4 edges/bit), at ~0.5 cps that is about
2 h 16 m; at 0.2 cps about 5 h 41 m. Use a lawful manufacturer-approved
check source for routine runs (minutes).
Optional faster extractor (interval parity / LSB of each gap, ~1 edge/bit):
uv run radioseed seed --profile mightyohm-check-source --no-passphrase \
--extractor interval-paritySee docs/entropy-design.md for the trade-offs.
- Motivation: @raw_avocado
- Demo: @Arceris_btc
- Hardware: MightyOhm Geiger Counter Kit
- Prior art: Apollo-NG / PiGI entropy from radioactive decay
- Outputs were historically checked against Sparrow and https://iancoleman.io/bip39/ — still: use at your own risk.