# Changelog All notable changes to the Bitcoin Research Kit (BRK) project will be documented in this file. > *This changelog was generated by Claude Code* ## [v0.3.0-beta.2](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.3.0-beta.2) - 2026-04-15 ### Breaking Changes #### `brk_types` - `BlockInfoV1` gained a `stale: bool` field (defaults to `false`, serialised alongside `info` and `extras`) that marks blocks which have been replaced by a longer chain after a reorg ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_types/src/block_info_v1.rs)) - Rewrote `Etag::from_series`: the signature changed from `(version, total, start, end, height)` to `(version, total, end, hash_prefix: BlockHashPrefix)`. Slices that reach the end of the series now produce `v{version}-{hash_prefix:x}` (tied to the current tip's reorg state), and bounded slices produce `v{version}-{total}` (stable until the series grows). The old `{start}-{end}` and `{start}-{total}-{height}` forms are gone ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_types/src/etag.rs)) #### `brk_iterator` - `BlockIterator::Item` changed from `Block` to `Result`. The `State::Rpc` arm now propagates `get_block_hash` / `get_block` errors as `Some(Err(e))` instead of ending iteration via `.ok()?`, and mid-iteration reorg detection returns `Some(Err(Error::Internal("rpc iterator: chain continuity broken (likely reorg mid-iteration)")))` so callers see the failure instead of a silent `None`. The `State::Reader` arm propagates `Result` from the new reader pipeline ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_iterator/src/iterator.rs)) #### `brk_computer` - Removed the `scripts` top-level module entirely (`scripts::Vecs`, `scripts::{count, compute, import, mod}`). The remaining `outputs_value` logic moved to `outputs/value/vecs.rs` and the lib.rs wiring no longer mentions scripts. Downstream consumers referencing `computer.scripts.*` must switch to the corresponding `outputs.*` paths ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/lib.rs)) - `blocks::lookback::Vecs::cached_window_starts` is no longer a public struct field. It is now a method `cached_window_starts() -> Windows<&WindowStartVec>`, and every downstream importer (inputs/outputs/mining/transactions/cointime/distribution) that used to pass `&blocks.lookback.cached_window_starts` now passes `&cached_starts` as a `Windows<&WindowStartVec>`. The companion `CachedWindowStarts` alias was replaced by the more explicit `Windows<&WindowStartVec>` type ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/blocks/lookback.rs)) - Removed `prices::Vecs::cached_spot_cents` and `cached_spot_usd` fields. The hot-path spot-price cache now lives on the `price_cents` `CachedPerBlock` itself, so every compute caller that referenced `prices.cached_spot_cents` / `prices.cached_spot_usd` in beta.1 has been migrated to use the cached fields on `price_cents.height` / `price_usd.height` directly ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/prices/mod.rs)) ### New Features #### `brk_reader` - Rewrote the blk-file streaming pipeline as a dedicated `pipeline` module. New files: `pipeline/mod.rs` exposing `DEFAULT_PARSER_THREADS` and `spawn(inner, canonical, parser_threads)`, `pipeline/forward.rs` driving the forward blk scanner, `pipeline/reorder.rs` handling out-of-order writes by bitcoind (up to `OUT_OF_ORDER_FILE_BACKOFF = 21` files), and `pipeline/tail.rs` streaming blocks that bitcoind appends live while the pipeline is running ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/src/pipeline/mod.rs)) - Added `canonical.rs` with a `CanonicalRange::walk(client, after_hash, tip)` helper that resolves the canonical chain hash list for the streaming pipeline, and `bisect.rs` with `first_block_height(client, blk_path, xor_bytes)` that binary-searches a blk file for the first canonical block height. Replaces the previous inline `find_start_blk_index` logic ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/src/canonical.rs)) - Added `parse.rs` as the single parsing entry point, replacing `decode.rs` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/src/parse.rs)) - Split `Reader::new` into `new` (raises NOFILE via `Self::raise_fd_limit()` then delegates) and `new_without_rlimit(blocks_dir, client)`, so CI and embedded consumers can opt out of the `setrlimit` call. `raise_fd_limit` is public, clamps to the hard limit, and warns (not panics) on failure — a `TARGET_NOFILE = 15_000` constant replaces the ad-hoc `max(15_000)` call ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/src/lib.rs)) - Added `Reader::all()`, `Reader::after(hash)`, and a new configurable `Reader::after_with(hash, parser_threads)` that return a `Receiver>` streaming canonical blocks from `hash + 1` (or genesis) to the tip. The old `read(start, end)` / `read_rev` API is gone ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/src/lib.rs)) - `ReaderInner::open_blk(blk_index)` is the new single entry point for opening blk file handles. `read_raw_bytes` switched to `FileExt::read_exact_at` (positional pread) and `reader_at` uses the new `XORIndex::at_offset(offset)` constructor ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/src/xor_index.rs)) - Added reader benchmark examples under `examples/`: `after_bench.rs` (measures full-chain streaming throughput), `last_n_bench.rs` (measures the tail-fetch path), `reader.rs`, `reader_single.rs`, and `blk_heights.rs` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_reader/examples/after_bench.rs)) #### `brk_error` - Added an `OptionData` extension trait with a single `data(self) -> Result` method that converts `None` into `Error::Internal("data unavailable")`. The doc comment explicitly notes it replaces `.unwrap()` in query paths so a missing value returns HTTP 500 instead of crashing the server under `panic = "abort"` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_error/src/lib.rs)) #### `brk_computer` - Added `internal::block_walker::walk_blocks(fi_batch, txid_len, coinbase, scan_tx, store)` with a `BlockAggregate` aggregate (totals plus `[u64; 12]` per-output-type entries/txs counters) and a `CoinbasePolicy::{Include, Skip}` control. Used by `inputs::by_type` and `outputs::by_type` to share a single per-block / per-type cursor walker, eliminating ~100 lines of duplicated loops ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/internal/block_walker.rs)) - Added `internal::with_addr_types::WithAddrTypes` — a generic `all + per-AddrType` container that mirrors the existing `WithSth` pattern along the address-type axis. It exposes `forced_import`, `min_stateful_len`, `par_iter_height_mut`, `reset_height`, `push_height(total, per_type)`, and `compute_rest(starting_indexes, exit)` which writes `all.height` as the per-block sum of the per-type vecs. Specialised `forced_import` impls exist for `PerBlock`, `PerBlockCumulativeRolling`, and similar families so every metric that tracks one aggregate alongside a per-address-type breakdown can use the same container ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/internal/with_addr_types.rs)) - Added a `CachedPerBlock` wrapper type in `internal::per_block::computed::cached`, used by `prices::Vecs::price_cents`. The wrapper carries an in-memory `CachedVec` over the on-disk `PerBlock` so every compute caller that previously reached for the separate `cached_spot_cents` field now reads through `price_cents.height` directly ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/internal/per_block/computed/cached.rs)) - Added `PercentCumulativeRolling` and `LazyPercentCumulativeRolling` under `internal::per_block::percent`, covering the new "cumulative + rolling window" percent metric shape used by several distribution modules ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/internal/per_block/percent/cumulative_rolling.rs)) - `blocks::lookback::Vecs` now wraps the `_24h`, `_1w`, `_1m`, and `_1y` lookback vecs in `CachedVec>` so the most frequently read lookback windows serve from an in-memory cache instead of re-reading from disk every block ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/blocks/lookback.rs)) - `internal::cache_budget::cache_wrap` is now generic over `V: TypedVec + ReadableVec + Clone + 'static`, matching the `CachedVec` wrapper migration in vecdb. Callers pass any cacheable source vec directly and get a `CachedVec` back ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_computer/src/internal/cache_budget.rs)) #### `brk_cohort` - `ByType::try_new(create)` constructor builds both the spendable and unspendable parts from a single `FnMut(Filter, &'static str) -> Result`, and the unspendable half now reuses the shared `OP_RETURN: &'static str` constant with `Filter::Type(OutputType::OpReturn)` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_cohort/src/by_type.rs)) - Added `ByType::iter`, `iter_mut`, `par_iter_mut`, `iter_typed`, and `iter_typed_mut` helpers so downstream metric modules can iterate all 12 output-type slots (11 spendable + op_return) without unpacking the struct manually. The existing `SpendableType` also grew matching methods ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_cohort/src/spendable_type.rs)) #### `brk_query` - Added `rustc-hash` dependency and new `indexed_height()` accessor that reads `blockhash.inner.stamp()` directly instead of computing `len - 1`, giving the server a stable, bounded way to pick the canonical tip height for cache strategies ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_query/src/lib.rs)) - Rewrote `tx.rs`, `block/txs.rs`, `block/info.rs`, `addr.rs`, and `mining/pools.rs` to thread `Result`s through every lookup path using the new `OptionData::data()` method and the refreshed `CachedVec` reader API. Null-safety audits that previously `unwrap()`'d through chained cursor reads now return `Error::Internal("data unavailable")` so the HTTP layer answers 500 instead of aborting the process ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_query/src/impl/tx.rs)) #### `brk_server` - Added `params::tx_index_param::TxIndexParam`, documented in the params module tree, so new endpoints can take a `TxIndex` path segment with schema-driven validation ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_server/src/params/tx_index_param.rs)) #### `brk_bindgen` / generated clients - Extracted a shared `BrkClientBase._getCached(path, parse, options)` method that runs the cache-vs-network race, invokes `onUpdate` when either branch resolves, and writes fresh responses back to the `caches` storage. Both `getJson(path, options)` and `getText(path, options)` now delegate to it with their own `parse` function (`res.json()` + `_addCamelGetters` for JSON, `res.text()` for text), giving CSV/text fetches the same caching, ETag short-circuit, and `onUpdate` semantics that JSON fetches already had. `getText` also gains `AbortSignal` and `onUpdate` option support ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_bindgen/src/generators/javascript/client.rs)) - Each generated `fetchCsv()` endpoint method now passes the same `{ signal, onUpdate }` options bag through `getText`, so `brkClient.series.*.fetchCsv({ signal, onUpdate })` benefits from browser-cache reuse and cancellation ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_bindgen/src/generators/javascript/api.rs)) #### `scripts` - Added a new `scripts/mempool_compat/` pytest project (managed with `uv`) that compares every brk mempool_space endpoint against the real mempool.space API using live blockchain data. It ships `conftest.py` + seven test modules covering addresses, blocks, fees, general, mempool, mining, and transactions, with its own `pyproject.toml` and lockfile. Nothing is hardcoded or deterministic — the tests pull live data on each run ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/scripts/mempool_compat/conftest.py)) #### `website` - Rewrote `options/mining.js`, `options/network.js`, `options/market.js`, `options/cointime.js`, `options/partial.js`, `options/full.js`, and the distribution sub-modules to match the new backend series layout (new per-addr-type cohorts, percent cumulative-rolling metrics, etc.). Added `options/series.js` and `options/shared.js` for shared builders ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/website/scripts/options/mining.js)) - Added a new `website/CLAUDE.md` with frontend-specific contribution notes, paired with a top-level `CLAUDE.md` update ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/website/CLAUDE.md)) - Assorted explorer fixes in `chain.js`, `index.js`, and search (`panes/search.js`) plus format/utils updates, accompanying the main/nav/explorer CSS polish ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/website/scripts/explorer/chain.js)) ### Internal Changes #### `brk_indexer` - `processor/{mod,tx,metadata}.rs` and `vecs/{blocks,transactions}.rs` updated to match the reader pipeline and `CachedVec` wrapper migration in vecdb — no user-visible behavior change ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_indexer/src/processor/mod.rs)) #### `brk_rpc` - `backend/{bitcoincore,corepc}.rs` refreshed for the new iterator error-propagation shape ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_rpc/src/backend/bitcoincore.rs)) #### `brk_oracle` - Refreshed examples (`compare_digits`, `determinism`, `noise`, `report`, `sweep_digits`, `sweep_tolerance`, `validate`) to compile against the new query result types ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.2/crates/brk_oracle/examples/validate.rs)) [View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.3.0-beta.1...v0.3.0-beta.2) ## [v0.3.0-beta.1](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.3.0-beta.1) - 2026-04-08 ### New Features #### `website` - Replaced the pinned/unpinned sidebar model with a split/full layout mode. The preference is stored under the new `split-view` localStorage key (replacing `sidebar-pinned`), and `data-layout` now holds `"split"` or `"full"` instead of `"desktop"`/`"mobile"`. A new inline script in `index.html` reads the preference synchronously before modules load and sets `data-layout` to avoid a flash of unstyled layout ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.1/website/scripts/main.js)) - Added a layout-toggle button (`#layout-button`, replacing `#pin-button`) that flips between split and full layouts on any device. `syncFrame()` now calls the relevant selector label when the layout changes, so the visible frame follows the mode switch automatically ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.1/website/index.html)) - Rewrote the desktop resize bar as `initResizeBar()` using pointer events (`pointerdown`/`pointermove`/`pointerup`/`pointercancel` with `setPointerCapture`), clamping the width to `--max-main-width * innerWidth`, persisting the value under `bar-width`, and resetting on double-click. Replaces the old `initDesktopResizeBar` which used three separate mouse listeners ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.3.0-beta.1/website/scripts/main.js)) - Moved `