
boha v0.20.2
Crypto bounties, puzzles and challenges data library
@agntn/puzzles
Public crypto puzzles and bounties, as typed records. You ask for a puzzle, you get its address, its key material and what happened on chain.
Why?
Every puzzle thread has the same three things: the addresses, the prizes, and who solved what. Every scanner and tracker re-types them from the thread, slightly differently each time. So they live here once, as code. A puzzle is a TypeScript record. The type checker reads it before a test does. The CLI, the MCP server and the Pi and OMP extensions read one registry.
Docs, one page per puzzle and a live playground: puzzles.agntn.dev.
[!WARNING] Pre-1.0. The API, the CLI flags and the data model can still move. Pin an exact version if you build on it.
✨ Features
- 🧾 Data as code. One
PuzzleSpecliteral per puzzle, built by onepuzzle()factory for every chain. No JSON, no build step. - 🕳️ Absent means absent. A puzzle without a solver or a prize has no such key. Nothing serializes as null.
- 🔑 Key material in every shape. Hex, WIF, a BIP38 payload, a seed phrase, secret shares, or just a bit width. One builder.
- 💤 Lazy registry. Importing the package loads no records.
get("bits/71")imports one collection module. - ✅ Verification is a value. A published key derives the address or it doesn't. Nothing throws for a bad record.
- 💰 Live balances.
puzzle.balance()through@agntn/explorers. Base units asbigint, API keys redacted from errors. - 👀 A watch on the record.
puzzles watchlists the deposits and spends a record misses, a prize that moved, a public key a spend gave away, a source page that changed. It never edits a record. You do. - 📋 A checklist before the weekend.
puzzles eligibilitygathers the source, the address, lifetime totals from the explorer, the status with its evidence and what counts as a solution. Whatever nobody can fill comes back as amissingrow, never a guess. - 🖼️ The puzzle itself, not a link to it.
puzzles_assetshands a model the image, the stage files and the archived posts behind the hints, checked against the SHA-256 the record pins. Links rot. These don't. - 🤖 Fifteen agent tools. One executor behind MCP, Pi and OMP. Same answer everywhere.
- 🌐 Runs anywhere. Neutral ESM on the Fetch API. Node, browsers, edge workers.
📦 Install
pnpm add @agntn/puzzles
Node.js 26 or newer for the CLI.
🚀 First call
npx @agntn/puzzles stats
That prints the total, one count per status and how many puzzles have a known public key. No key, no config, no network. The records ship inside the package. The bare puzzles below is pnpm exec puzzles after a local pnpm add, or just puzzles after pnpm add -g @agntn/puzzles.
puzzles show hash-collision/sha256
hash-collision/sha256 unsolved 0.27734251 BTC 35Snmmy3uhaer2gTboc81ayCip4m9DT4ko
chain: bitcoin address kind: p2sh
hash160: 292fb39df7cd619a396069383928e6bfb74ebec5
redeem script: 6e879169a87ca887 (hash 292fb39df7cd619a396069383928e6bfb74ebec5)
public key: unknown
private key: unknown
started: 2013-09-13 05:59:09
transactions: 1
funding 2013-09-13 05:59:09 0.1 BTC 397f12ee15f8a3d2ab25c0f6bb7d3c64d2038ca056af10dd8251b98ae0f076b0
explorer: https://blockstream.info/address/35Snmmy3uhaer2gTboc81ayCip4m9DT4ko
source: https://bitcointalk.org/index.php?topic=293382.0
Commands
| Command | What it prints |
|---|---|
puzzles stats | Totals, status counts, prize sums and the data version |
puzzles collections | One row per collection: key, counts, author |
puzzles authors [key] | One row per author, or one author's record with its sourced facts |
puzzles author <key> | One author's record, straight from the puzzles_author tool |
puzzles solvers [key] | One row per named solver, or one solver's record: every solve, profiles and sourced facts |
puzzles solver <key> | One solver's record, straight from the puzzles_solver tool |
puzzles show <id> | One puzzle's record: key material, transactions, hints and links. --json for the data |
puzzles hints <id> | The collection's hints, the puzzle's own, then its hint files. --json for both |
puzzles stages <id> | The stages of a multi-stage puzzle with their pages, files and published answers. --json for the list |
puzzles assets <id> | The files a puzzle ships with SHA-256 and size, and the archived pages it cites. --read <path> writes one, --check/--live verify |
puzzles list [collection] | One puzzle per line. --address, --chain, --status, --technique and --with-pubkey narrow it, --limit and --offset page it |
puzzles verify [id] | A published key and its recipe against the address. --all or the list filters for a set, exit 1 on a mismatch |
puzzles balance [id] | The live balance. The list filters check a whole set, one row each. --api-key, or one variable per chain |
puzzles watch [id] | What the chain knows and the record doesn't. --since checks the source pages too, exit 1 on any finding |
puzzles eligibility <query> | The checklist before working on a prize, by id or address. Exit 1 while any field is missing |
puzzles export | The whole dataset with its data_version |
puzzles mcp | The MCP server over stdio |
--json prints the data behind the text, bigint as strings and absent fields left out. The commands come from the same tool definitions the agents get, so a typo in a flag fails here just like it fails for a model. The flags and exit codes are in the CLI guide.
🧠 Library
import { get, stats, verify } from "@agntn/puzzles";
import { bits } from "@agntn/puzzles/collections/bits";
const puzzle = await get("bits/71"); // loads the bits collection, nothing else
puzzle?.address().value; // "1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU"
puzzle?.keyRange(); // [2n ** 70n, 2n ** 71n - 1n]