A command for somebody who holds a mnemonic and does not know what it is: which scheme it follows, which addresses it gave, which coins those addresses held, and whether a fork concerns them. It finds funds; the user's own wallet moves them.
No release is out yet. seed-scan identify, seed-scan scan,
seed-scan forks and seed-scan report are the first commands.
btclib-org/.github#2203
records the design and tracks the first release. The command is to stay
thin: what is general goes down to the packages it is to build on,
bip39-slip39,
bip32-descriptors,
btclib-wallet and
coin-addresses. A
scheme or a coin they do not cover is to be reported as not checked,
never passed over: the costly failure is a wrong "nothing found".
seed-scan identify > bundle.jsonIt asks for the mnemonic, then for passphrases until an empty one, at
hidden prompts, and for further shares where the words are one
SLIP-0039 share. It writes the bundle to standard output and what it
found to standard error. Where you know an address the mnemonic gave,
--address followed by it has the command search for the address's
path. seed_scan.bundle documents the bundle, and
seed-scan identify --help the bounds of what it writes and searches.
seed-scan scan bundle.json --server electrum.example.org:50002 \
--node http://127.0.0.1:8545 > scan.jsonIt asks the Electrum server named, as host:port, which Bitcoin
accounts of the bundle were used, and the Ethereum node named, by its
URL, which Ethereum accounts were, and writes the scan result to
standard output and what it found to standard error. Name either or
both. Each learns every address it is asked about, so name your own:
there is no default.
The Electrum connection is TLS, and the server's certificate is verified
against the system's CA certificates, or against those of the file
--cafile names, which is how a server with a self-signed certificate
is reached. Before any address is asked, the server's genesis block and
its block at a height Bitcoin Core pins are compared with Bitcoin's.
SECURITY.md's Limitations, not vulnerabilities says which servers that
refuses, and what it does not prove.
The node is an execution client's JSON-RPC, at an https URL, or an
http one on a loopback address such as http://127.0.0.1:8545.
--cafile is the Electrum server's alone: seed_scan.evm says what the
node's certificate is verified against. An Ethereum address is used
where it holds ether or has a nonce: token balances are not asked, so an
address that only ever received tokens reads as unused.
A coin with no server or node named, and every coin but Bitcoin and
Ethereum, is listed as not checked. seed_scan.scan documents the
result, seed_scan.evm the node's requests, and seed-scan scan --help
the bounds of the search.
seed-scan forks bundle.json --chain bitcoin-cash \
--server bch.example.org:50002 > forks.jsonCoins a key held on Bitcoin when a chain forked from it are on that
chain too, at the same script, and the chain's own wallets may have used
the key since. forks asks the Electrum server named, which serves the
chain named, which of the bundle's accounts were used there, and writes
the forks result to standard output and what it found to standard
error. Bitcoin Cash is the chain it reads. How it reads an output that
also carries CashTokens is in seed_scan.forks.
The server is the user's own, and its certificate is verified as Scanning a bundle says. Before any address is asked, its blocks at the chain's checkpoints are compared with the chain's, so that a server of a chain whose blocks differ there, as one of Bitcoin's, is refused. That shows which chain the server says it serves, not that the histories it answers are that chain's: the server is trusted for what it answers, as SECURITY.md's Limitations, not vulnerabilities says, and proving each history against the chain is btclib-org/seed-scan#34.
seed_scan.forks documents the result and which accounts are read, and
seed-scan forks --help the bounds of the search.
seed-scan report bundle.json --scan scan.json > report.txtIt reads the bundle and the scan result made from it, offline, and
writes what they say to standard output: for each reading, coin and
purpose, the used accounts and each of their used addresses, with its
balance and path, and what was not checked and why. Without --scan,
as after a server or a node failed, every account is listed as not
checked. A result made from another bundle is refused. The last line is
the SHA-256 of the lines above it. seed_scan.report documents the
layout.
The commands are to keep to this split, so that the mnemonic stays on one machine and everything after it reads public data:
identifytouches the secret and no network. It reads the mnemonic from a prompt, never from an argument. It tries each scheme, language and candidate passphrase, and, given an address the user knows, finds the reading and the path that give it. It writes a public bundle: the fingerprint and the account public keys, or the addresses where a coin has no extended public key.scanandforkstouch the network and no secret. They read the bundle and what the commands before them wrote, and the CA certificates they verify their server against, andscanits node: the system's or, for a server, those of the file the user names; nothing else. Scanning a bundle and Reading a fork of Bitcoin say which server and which node each asks.forksreads the bundle's accounts on a chain forked from Bitcoin, and takes what each held at the fork from the history the chain's server answers.reporttouches neither. It assembles whatscanfound: what was searched, what was found, the limits of the search, and the digest of the report. It reads no result offorks, which is btclib-org/seed-scan#32.
The bundle holds no secret, but it is not harmless: an account public key gives every address of that account, so whoever holds the bundle can follow its history and balance, though not spend from it. The same holds of the report: it holds no secret, but it lets anyone follow the wallet's history.
The package is fully annotated and ships py.typed. Its refusals are
seed_scan.exceptions's classes: a ValueError or a TypeError,
under one base, SeedScanException.
from seed_scan.exceptions import (
SeedScanException,
SeedScanTypeError,
SeedScanValueError,
)
assert issubclass(SeedScanValueError, (SeedScanException, ValueError))
assert issubclass(SeedScanTypeError, (SeedScanException, TypeError))tests/readme_test.py runs every Python block of this file, so the
example above is the one the suite checks.
SECURITY.md says what the package does to guard the mnemonic and what it cannot, how to report a vulnerability, which versions are supported, and how a published file is traced back to the run that built it.
Development is LLM-based: humans are welcome, as long as they are
comfortable with agentic development ;-). A contribution must have been
read and understood by the person who submits it.
CONTRIBUTING.md
has the rules.
CONTRIBUTING.md
has the commands each CI job runs, verbatim. uv sync creates the
environment; uv is the only tool that has to be installed.
REVIEWING.md
is what a pull request is answered against.
How the organization decides, and who holds which role, is its GOVERNANCE.md; what it intends to do, and what it deliberately does not, is its ROADMAP.md.
- Source: https://github.com/btclib-org/seed-scan
- Releases: https://github.com/btclib-org/seed-scan/releases
- CHANGELOG.md, and RELEASE_NOTES.md for what a release asks a user to act on
The btclib organization and its projects are actively supported by DGI and CheckSig.