konar est. 2026
CASE STUDY · 02
BITCOIN DATA INFRASTRUCTURE  ·  2024 — 2025  ·  SOLE ENGINEER

Nine repositories. Four protocols. One engineer.

Satstream indexes the Bitcoin chain into a queryable API. Four protocol indexers (BRC-20, Runestones, Charms, and a Delta state engine) write into a 4-database cluster for sub-second queries, exposed through a 14-category API surface, five SDKs, an MCP server, and a block explorer. The BRC-20 indexer handles the cursed-inscription edge case where UniSat, Hiro and OKX each diverge from the spec in different ways. 376 commits across nine production repos.

COMMITS
376
9 production repos
PROTOCOLS
4
brc-20 · runes · ord · charms
API SURFACE
14 categories
go · fiber · openapi-generated sdks
DATABASES
4
mongo · scylla · pg · redis
SDKs
5
go · js · python · rust · ts

Bitcoin's RPC is fine for nodes and miserable for applications. Anyone shipping a wallet, explorer or DeFi product against BRC-20, Runes or Ordinals ends up writing the same indexer twice: once badly, once again with the lessons. Satstream is that indexer, written once, operated as infrastructure, exposed as a clean REST API and as an MCP server for AI agents.

  ┌─ bitcoin node (full archival)
  │
  ├──▶ ss-indexer            [ go · mongo · grpc ]   core blocks · runic / non-runic split
  │      └──▶ runestone-decoding  [ grpc microservice · LEB128/big.Int decoder ]
  │
  ├──▶ satstream-brc20-indexer    [ rust · mongo ]   brc-20 ops · cursed-inscription edge case
  │
  ├──▶ satstream-delta-indexer    [ rust · scylladb · prometheus ]   per-address sat deltas
  │      gap scan + block continuity verification across 800K+ blocks
  │
  └──▶ charms-extractor           [ python · cassandra ]   ordinals charm attributes
                                             │
                                             ▼
                             ┌─ ss-api  [ go · fiber ] ──┐
                             │   14 endpoint categories  │
                             │   mongo · scylla · redis  │
                             │   stripe metering         │
                             └────────────┬──────────────┘
                                          │
      ┌───────────┬───────────┬───────────┼────────────┬────────────┐
      ▼           ▼           ▼           ▼            ▼            ▼
  sdk · go    sdk · js    sdk · py    sdk · rust    sdk · ts    ss-mcp (bun)
      (5 sdks generated from one openapi spec)               llm-native surface
01  ·  DECISION
Cursed-inscription edge case in BRC-20

Most indexers treat a transaction with a cursed inscription as dead and skip the BRC-20 op. The BRC-20 spec says otherwise: a cursed inscription still carries a valid transfer of an existing active inscribe-transfer. The Rust indexer tracks an all_inscriptions_cursed flag per transaction, loads a HashSet of known cursed TXIDs at startup, and continues to dispatch transfer operations when every inscription in a TX is cursed. Getting this wrong produces divergent user balances. UniSat, Hiro, and OKX have all shipped different interpretations of the same spec.

02  ·  DECISION
LEB128/big.Int Runestone decoder with dual-path parser

The Runes protocol encodes amounts as little-endian base-128 varints (LEB128) with values up to 2^128. The Go decoder uses big.Int throughout, not uint64, because the UNCOMMONGOODS cap is 340282366920938463463374607431768211455. It also handles dual-path input: Bitcoin Core returns either raw hex (6a5d...) or asm-tokenized (OP_RETURN 13 <hex>) depending on node version and verbosity level, and the parser falls back to hex when asm decoding fails. The UNCOMMONGOODS genesis rune is hard-coded at block 840,000 with exact cap, height range, turbo, and spacers. If that initialization is wrong, every downstream Runes balance is wrong.

03  ·  DECISION
Adaptive concurrency tied to MongoDB shard load

The adaptive concurrency controller implements a live-adjusting semaphore. Every 10 seconds it queries MongoDB's serverStatus across all shard clients, computes connection load ratio and average commit latency in microseconds, then increments or decrements the worker pool capacity (bounded [1, 100]) via a sync.Cond-based CountingSemaphore. Hard-coded thread counts get wedged when the database is slow. This controller backs off the indexer in proportion to actual DB saturation rather than guessing a ceiling at deploy time.

04  ·  DECISION
Full partial-block rollback on sync resumption

The BRC-20 indexer does not resume by simply appending. On startup it detects the last completed block height, then deletes every record at or above that height across 6 collections (deploys, mints, transfers, invalids, tickers, user balance entries), resets total_minted for affected tickers, and rebuilds user balances from the remaining entries. The delta indexer adds a gap scan: verify_block_continuity() walks every height from 0 to max as a BTreeSet and re-processes any missing blocks before the live tail starts. Without this, an interrupted sync leaves gaps that silently produce wrong UTXO balances.

05  ·  DECISION
Ord server HA without an external load balancer

The Ord client pool in ss-api maintains one main client and N backups, each with its own HTTP connection pool. A background goroutine health-checks each server on a configurable interval and marks failed nodes. getClient() returns the first healthy client, falling back to backups in round-robin. Inscription queries survive ord-server restarts with no ELB or ALB in front and no manual intervention. Failure state is consistent even under concurrent request load because the URL manager is shared across all clients.

06  ·  DECISION
MCP server exposes the full API surface to AI agents

Shipped ss-mcp alongside the SDKs. The complete 14-category API is callable by Claude, Cursor, and any MCP-aware agent without a custom integration. UTXOs, inscriptions, BRC-20 ops, Runes, and Charms are now a native tool call. New onramp, same backend, zero additional indexing work.

Satstream required simultaneous depth in Bitcoin protocol internals (UTXO model, BRC-20 spec including the cursed-inscription edge case, Runestone LEB128 encoding, inscription model) and production distributed systems (ScyllaDB write throughput, gRPC service boundaries, Prometheus instrumentation, AWS CDK). The adaptive concurrency controller in ss-indexer reads live MongoDB shard load to tune its own worker pool in real time. The MCP server extends the data layer to AI agents. Bitcoin state is a native tool call for any MCP client, no custom integration.