Guide

CLI

The puzzles command. Seventeen subcommands and what each prints. Which ones exit 1 and what --json gives a script.

Run it

shell
pnpm add -g @agntn/puzzles
puzzles stats
text
Total: 558
Solved: 325
Unsolved: 113
Claimed: 20
Swept: 96
Expired: 4
With pubkey: 449
Techniques: aes 3, ascii-private-key 1, atbash 14, base64 2, beaufort 1, binary 6, bip38 4, caesar 2, hash-collision 5, hidden-seed-words 20, masked-key-range 256, md5-to-bip39-entropy 140, morse 3, openssl-salted-sha256 1, partial-key 6, qr 1, scrypt-brainwallet 1, sha256-brainwallet 31, sha256-to-bip39-entropy 19, shamir-shares 1, steganography 22, straddling-checkerboard 1, triple-sha256-brainwallet 3, warpwallet 6, xor 7
CommandDescription
puzzles statsTotals, status counts, prize sums and the data version, the same text puzzles_stats hands a model
puzzles collectionsOne line per collection: key, count, the count per status, author. --json puts them under collections
puzzles authors [key]One line per author, or one author's record by author key or collection key: aliases, profiles, addresses, sourced facts
puzzles author <key>The puzzles_author tool as a command, one author's record by author key or collection key
puzzles solvers [key]One line per named solver, or one solver's record by solver key or puzzle identifier: every solve with date and prize, profiles, sourced facts
puzzles solver <key>The puzzles_solver tool as a command, one solver's record by solver key or puzzle identifier
puzzles show <id>One puzzle's record: address, key material, transactions, stages and hints, folded past three per block, links. --all-transactions unfolds the dust, --json for the data
puzzles hints <id>The collection's hints first, then the puzzle's own, then its hint files. --json for both
puzzles stages <id>The stages of a puzzle that runs in several: what each shows, its pages and files, and the published answer when there is one. --json for the list
puzzles assets <id>The files a puzzle ships with SHA-256 and size, then the archived pages it cites. --read <path> writes one to stdout, --check <dir> hashes your copies, --live the author's URLs
puzzles list [collection]Puzzles, one per line, --address, --chain, --status <status>, --technique <name> and --with-pubkey to narrow, --limit <n> and --offset <n> to page, --json for the records
puzzles verify [id]A published key and its recipe against the address, or a set by --collection and the list filters, --all for every puzzle, --quiet for the exit code alone
puzzles balance [id]The live balance through the chain's explorer, or --collection and the list filters for one row per puzzle, --api-key for Ethereum
puzzles watch [id]What the chain knows and the record doesn't, by id or by the list filters. --since <date> compares the source pages too
puzzles eligibility <query>The checklist before working on a prize: record, live totals, status with evidence, carriers, verifier, a missing row per gap
puzzles exportThe complete dataset envelope, --compact without indentation
puzzles mcpRun the MCP server over stdio

Examples

shell
puzzles list bits --status unsolved              # one line per unsolved puzzle: id, status, prize, address
puzzles list --with-pubkey --json | jq -r '.[].id' # every puzzle with a known public key
puzzles list --no-with-pubkey --status solved      # solved, and still no public key on record
puzzles list --technique atbash                    # every puzzle with an Atbash step
puzzles list --chain ethereum                      # every Ethereum record, wherever it lives
puzzles list --address 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH # which puzzle is this? bits/1
puzzles list bits --limit 5 --offset 70           # five rows from bits/71, not the whole collection
puzzles show hash-collision/sha256 --json          # the record, redeem script included
puzzles hints bits/71                             # the author's one statement about the keys
puzzles verify bits/135                           # OK  bits/135
puzzles verify --collection rushwallet             # every SHA-256 brainwallet rerun, one SKIP without a key
puzzles assets gsmg --check ./gsmg               # MATCH, MISMATCH or MISSING per file, exit 1 unless every copy matches
puzzles assets gsmg --read assets/gsmg/phase3.txt # the page itself, only if its bytes match the pin
puzzles solvers bits/135                          # the solver behind it: RetiredCoder, four solves
puzzles balance --status unsolved                  # OK bits/71 7.10195986 BTC, one row per open prize
puzzles watch --status unsolved --since 2026-09-01 # DEPOSIT, SPEND, BALANCE, PUBKEY, SOURCE, PARTIAL or OK per puzzle
puzzles eligibility bits/71                       # live totals, a conflict row, the key range, exit 0
puzzles export --compact > puzzles.json            # a snapshot with its data_version
puzzles stats --json | jq -c .unsolved_prize       # {"AR":…,"ETH":…,"BTC":…}, what is still on the table

A collections row prints the key, the puzzle count, the statuses and the author, bits: N puzzles, N solved, N unsolved, N swept, by saatoshi_rising: solved and unsolved always, then claimed, swept and expired when the collection has any, so the counts add up to the total. A list row, and the first line of show, is tab separated: bits/71 unsolved 7.10195986 BTC 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU. A puzzle without a recorded prize prints - in the third column. The rest of show is one name: value field per line, the same text puzzles_show hands a model. Statuses for --status are unsolved, solved, claimed, swept and expired. Anything else is an error, not an empty list. --limit and --offset page the rows the way puzzles_list pages them for a model: the offset skips matching puzzles after the filters, in dataset order. There is no default limit, a bare list still prints every match. Both want an integer, --limit 0 or --offset -1 is an error. --address goes the other way: you have an address and want the record behind it. Case matters only where the chain says it does, so an EIP-55 Ethereum address and a shouted bech32 one both find their puzzle, while a recased base58 string does not, because that is a different address.

Exit codes

show, hints, stages, assets, verify, balance and a list with an unknown collection print a message and exit 1. verify --all, and a filtered verify, exit 1 when any key or recipe derives the wrong address. Filters that match nothing exit 1 too, because checking nothing isn't a pass. Puzzles without a key don't count, which is what a CI gate wants. A filtered balance exits 1 when any lookup failed, after it tried every puzzle. watch exits 1 on any finding and 2 when the only news is a failed lookup. A cron job can tell the two apart. eligibility exits 1 when any field is missing, so the exit code says whether the record is complete. assets --check exits 1 when a copy is missing or has other bytes. assets --read exits 1 when no copy answers with the pinned bytes, and stdout stays empty, so a redirect never saves a wrong file. assets --live exits 1 only on other bytes: an author URL that answers 404 or times out prints UNREACHABLE with the reason, because a dead host says nothing about the file, and a file the record has no author URL for prints NO_ORIGIN. list with a status that matches nothing prints nothing and exits 0. Don't use an empty list as a check. A flag a command doesn't take is an error too. verify bits/1 --key abc has no --key, so it exits 1 and names it instead of checking the stored key and printing OK. The same goes for a second ID after show. And a misspelled flag never sneaks into the ID. list --withPubkey names the option instead of hunting for a collection called --withPubkey. mcp still shrugs off whatever flags a client config throws at it. Only --help gets the usage.

shell
puzzles verify --all --quiet && echo "every published key derives its address"

A closed pipe ends the process quietly: puzzles export | head doesn't crash with an EPIPE trace. So what does --help load? The command definitions, and that's it. Each command imports its modules once it runs. Listing them loads no collection, no curve and no MCP SDK.

Machine output

--json prints the details a command answers with, through the same serializer every command shares. bigint comes out as a string and absent fields stay out. Nothing on the CLI prints data through a logger.

balance and watch print each row the moment its lookup lands. Who wants to stare at a silent terminal through a hundred lookups? Under --json they wait and print one array instead, valid to the last bracket. assets --read --json gets refused before a single byte goes out. A PNG with JSON glued to its tail is just a broken PNG.