harpy
A proof-of-work blockchain in Crystal โ UTXO transactions, P2P gossip, reorgs, and a Merkle anchoring layer, built for learning without cutting corners.
๐ Quick start ยท ๐ Guided demo ยท โ Anchoring ยท ๐ Documentation
The built-in dashboard at /dashboard โ mining three blocks against a live node.
Harpy is a from-scratch blockchain node โ signed UTXO transactions, a mempool, difficulty retargeting, cumulative-work fork choice with reorgs, peer-to-peer block gossip, and a hash-anchoring/verification layer with SPV light-client proofs. It's an educational project (single operator, not mainnet-grade), but every layer is built and tested the way a real node would be: adversarial specs, a formally model-checked consensus core, and a chaos harness. Named after Harpocrates, the Greek god of silence.
What it is not: production financial infrastructure. No privacy, no external audit, no economic guarantees. Run it to understand how a chain works โ see docs/THREAT_MODEL.md for the honest security posture.
Features
Transactions & cryptography
- ๐ธ UTXO model โ signed inputs/outputs, coinbase with 100-block maturity, fees, double-spend prevention (STATE_MODEL.md)
- ๐ Ed25519 signatures with a crypto-agile address format (versioned, algorithm-tagged, checksummed) so post-quantum schemes are additive, not breaking (POST_QUANTUM_MIGRATION.md)
- ๐งพ Mempool with validation, fee ordering, and conflict rejection
Consensus
- โ๏ธ Proof-of-work with configurable difficulty and time-based retargeting
- ๐๏ธ Cumulative-work fork choice โ heaviest chain wins, not longest (SELFISH_MINING.md, CONFIRMATION_DEPTH.md)
- ๐ Reorgs with per-block undo logs for exact UTXO rewind
Networking
- ๐ P2P block gossip with an orphan pool and multi-node convergence (P2P.md)
- ๐ก๏ธ Network hardening โ peer caps, misbehavior banning, eclipse-attack countermeasures (SYBIL_RESISTANCE.md, ROUTING_PARTITION.md)
Storage & integrity
- ๐พ Atomic writes (temp-file + rename) behind a swappable storage backend
- ๐ Checksum envelope โ bit-rot / tamper detected on load, before validation (STORAGE_BACKENDS.md)
Verification layer
- โ Merkle anchoring API + SDK โ commit external record hashes on-chain, get inclusion proofs (AUDIT_LOG_ANCHORING.md)
- ๐ชถ SPV light clients โ verify a genesis-and-tip-pinned header chain + Merkle path, no full node
Assurance
- โ TLA+ consensus spec, model-checked with TLC โ the fork-choice safety property holds across the state space (spec/tla)
- ๐ช๏ธ Chaos harness โ partition/heal, crash/restart, and Byzantine-block fault injection (spec/chaos_harness_spec.cr)
- ๐ Opsec docs โ node hardening, key rotation, threshold multisig design (NODE_HARDENING.md)
Quick start
Prerequisites: Crystal โฅ 1.12. On Windows: winget install CrystalLang.Crystal, enable Developer Mode (for shards symlinks), restart your terminal, or run .\scripts\setup.ps1.
shards install
crystal run src/harpy.cr # starts an HTTP node on 127.0.0.1:3000
Then open http://127.0.0.1:3000/dashboard โ a branded status page with live chain stats and one-click mining (shown in the GIF above).
Mine your first block and inspect the chain:
# generate a keypair for the coinbase payout, then:
curl -X POST http://127.0.0.1:3000/mine \
-H "Content-Type: application/json" \
-d '{"miner_pubkey":"<64-hex-ed25519-pubkey>"}'
curl http://127.0.0.1:3000/ # full chain
curl http://127.0.0.1:3000/validate # {valid, height, work, tip}
New to it? Walk through docs/DEMO.md for a guided tour with real payloads.
HTTP API
| Method & path | Auth | Description |
|---|---|---|
GET /dashboard |
โ | Branded live status page (see GIF above) |
GET / |
โ | Full blockchain as JSON |
GET /health |
โ | Chain validity, last-save time, P2P/eclipse status |
GET /validate |
โ | Validity, height, cumulative work, tip hash |
GET /block/:index |
โ | A single full block |
GET /header/:index |
โ | Block header only (light-client sync) |
GET /headers?from=&to= |
โ | Range of headers |
GET /proof/:index/:txid |
โ | SPV inclusion proof (header + Merkle path) |
GET /mempool |
โ | Pending transactions |
GET /anchor/:record_hash |
โ | Inclusion proof for an anchored record |
POST /tx |
๐ | Submit a signed transaction to the mempool |
POST /mine |
๐ | Mine a block { "miner_pubkey": "โฆ" } |
POST /anchor |
๐ | Anchor a record hash { "record_hash": "โฆ" } |
๐ = requires Authorization: Bearer <HARPY_API_KEY> (or X-API-Key) when HARPY_API_KEY is set. Write endpoints are rate-limited. See NODE_HARDENING.md.
Anchoring: hash-on-chain, data off-chain
Harpy's endgame is a verification layer: commit a record's hash on-chain and keep the data anywhere. A light client verifies it from the complete header chain between a caller-pinned genesis and independently obtained trusted tip/checkpoint, plus a Merkle proof โ no full node required. Genesis pinning alone does not establish canonicality.
crystal run examples/audit_log_anchoring.cr # anchors log lines, proves inclusion, โ DEMO OK
client = Harpy::AnchorClient.new(trusted_genesis_hash, trusted_tip_hash, "http://127.0.0.1:3000")
client.submit(digest) # queue a record hash โ next POST /mine seals it into the block
client.verify(digest) # verify proof against the independently pinned tip โ true
Full walkthrough: docs/AUDIT_LOG_ANCHORING.md.
Configuration
All configuration is via environment variables.
| Variable | Purpose | Default |
|---|---|---|
HARPY_DIFFICULTY |
Genesis PoW difficulty (new chain only) | 3 |
HARPY_GENESIS_TIMESTAMP |
Deterministic v3 genesis timestamp | 2026-07-20 00:00:00 UTC |
HARPY_DATA_DIR |
Chain file path or parent directory | data/chain.json |
HARPY_API_KEY |
Write auth for POST /tx, /mine, /anchor |
(unset = open) |
HARPY_RATE_LIMIT |
Max write requests per client per window | 2 |
HARPY_RATE_LIMIT_WINDOW |
Token-bucket refill interval (seconds) | 10 |
HARPY_BIND_HOST |
HTTP bind address | 127.0.0.1 |
HARPY_HTTP_PORT / PORT |
HTTP port | 3000 |
HARPY_TRUST_PROXY |
Honor X-Forwarded-For (trusted proxy only) |
off |
HARPY_P2P_DISABLE |
Set 1 to disable P2P |
off |
HARPY_P2P_PORT |
P2P TCP port | 9333 |
HARPY_P2P_PEERS |
Comma-separated bootstrap peers | โ |
An absent HARPY_API_KEY permits unauthenticated writes only for the default loopback development setup. An explicitly empty or whitespace-only value is a startup error; the Docker testnet always requires a non-empty key. | HARPY_ANCHOR_PEERS | Trusted peers for eclipse countermeasures | โ |
Multi-node on one host:
HARPY_DATA_DIR=/tmp/a.json HARPY_HTTP_PORT=3000 HARPY_P2P_PORT=9333 crystal run src/harpy.cr
HARPY_DATA_DIR=/tmp/b.json HARPY_HTTP_PORT=3001 HARPY_P2P_PORT=9334 \
HARPY_P2P_PEERS=127.0.0.1:9333 crystal run src/harpy.cr
CLI
Running with no arguments starts the node; subcommands operate on a chain file and exit non-zero on failure (CI-friendly):
crystal run src/harpy.cr -- verify-chain --path data/chain.json
crystal run src/harpy.cr -- export-chain --path data/chain.json --out backup.json
crystal run src/harpy.cr -- help
Architecture
flowchart LR
W[Wallet / SDK] -->|POST /tx /mine /anchor| API[HTTP API]
L[Light client] -->|GET /header /proof /anchor| API
API --> MP[Mempool]
MP --> MINER[Miner - PoW]
ANCHOR[Anchor - Merkle roots] --> MINER
MINER --> CHAIN[Chain - fork choice and reorg]
CHAIN --> STATE[UTXO state and undo log]
CHAIN --> STORE[(Storage - atomic and checksum)]
CHAIN <-->|block gossip| P2P[P2P - orphan pool and peers]
P2P <--> PEERS[Other nodes]
src/harpy.cr # entry point โ CLI dispatch / server
src/harpy/ # block, block_header, chain, state, utxo, mempool, miner,
# transaction, crypto, merkle, spv, anchor, difficulty, storage, server
src/harpy/storage/ # backend interface + atomic file backend
src/harpy/p2p/ # gossip, protocol, orphan pool, peer manager, eclipse, reputation
public/dashboard.html # branded live status page (GET /dashboard)
public/design-system/ # full Harpy design system (tokens, fonts, icons, components)
spec/ # unit + integration + chaos specs; spec/tla/ TLA+ model
examples/ # runnable demos (audit-log anchoring)
docs/ # design, security, and ops documentation
Documentation
| Topic | Doc |
|---|---|
| Guided demo / runbook | DEMO.md |
| UTXO state & transactions | STATE_MODEL.md |
| P2P gossip, reorgs, troubleshooting | P2P.md |
| Storage integrity & backends | STORAGE_BACKENDS.md |
| Merkle anchoring use case | AUDIT_LOG_ANCHORING.md |
| Threat model | THREAT_MODEL.md |
| Node hardening runbook | NODE_HARDENING.md |
| Key rotation & compromise response | KEY_ROTATION.md |
| Threshold multisig design | THRESHOLD_MULTISIG.md |
| Post-quantum migration plan | POST_QUANTUM_MIGRATION.md |
| Consensus theory | SELFISH_MINING.md ยท CONFIRMATION_DEPTH.md ยท FINALITY.md |
| Network attacks | SYBIL_RESISTANCE.md ยท ROUTING_PARTITION.md ยท POS_CHECKPOINTING.md |
| Incident response | INCIDENT_RESPONSE.md |
| Brand & design system | BRAND.md |
| Formal verification (TLA+) | spec/tla/README.md |
| Agent-oriented guidance | AGENTS.md |
Development
crystal spec # run the test suite
crystal tool format # format sources
shards build # build the bin/harpy binary
Model-check the consensus spec (needs Java + TLC):
cd spec/tla
java -cp tla2tools.jar tlc2.TLC -nowarning -config HarpyConsensus.cfg HarpyConsensus.tla
# โ Model checking completed. No error has been found.
Status
Harpy is feature-complete across its educational roadmap:
| Layer | Status |
|---|---|
| PoW blocks, hashing, validation | โ |
| UTXO transactions, Ed25519, mempool, fees | โ |
| Difficulty retargeting, cumulative-work fork choice | โ |
| P2P gossip, orphan pool, reorgs with undo | โ |
| Atomic storage + checksum envelope | โ |
| Merkle anchoring API, SPV, block headers | โ |
| Crypto-agile addresses, node opsec docs | โ |
| TLA+ model-checked consensus + chaos harness | โ |
Explored but out of scope: embedded-KV storage backend, a smart-contract VM, cross-chain bridges โ see the docs and roadmap issues.
License
MIT โ see shard.yml. An educational project; use at your own risk.
harpy
- 1
- 0
- 42
- 0
- 2
- 2 days ago
- July 2, 2026
Fri, 24 Jul 2026 18:57:20 GMT