# penumbra-1 history archive

Everything needed to rebuild a full-history Penumbra node, event index (`cometbft` psql schema), `pindexer` or explorer from genesis, without depending on any live node. Served at https://snapshot.rotko.net/penumbra-1/history/ (https + torrents with webseed; `manifest.json` here is machine-readable, every file has a `.sha256` and a `.torrent`).

## Why "eras": what an upgrade destroys

Penumbra upgrades are checkpoint upgrades. At each planned boundary every node runs `pd migrate`: the application state (the JMT in `pd/rocksdb`) is transformed in place, a **new genesis** is written whose `app_state` is only a checkpoint of the post-migration app hash, `initial_height` is set to the next height, and the CometBFT block store, state.db and WAL are **deleted**. The new chain (same `chain_id`, new genesis) starts with an empty block store, so the old blocks stop existing on the network. The 2026-09-02 halt was resolved the same way (`pd migrate-restart`, pd 2.0.9): last old block 12,598,600, a synthetic empty application block 12,598,601 (executed by pd, never a CometBFT block), new genesis with `initial_height` 12,598,602.

Consequently, replaying history across a boundary needs three things per era:

1. **the block archive** of the ending era (all CometBFT blocks up to its last height),
2. **the application state at the boundary** (a node archive taken at the last block, before or after the migration), and
3. **the genesis of the next era** (the checkpoint genesis).

penumbra-1 eras (heights are the last block of each era):

| era | app version | blocks | genesis |
|---|---|---|---|
| 1 | v0.79 | 1 – 501,974 | `genesis-0.json` (full content) |
| 2 | v0.80 | 501,975 – 2,611,799 | `genesis-1.json` |
| 3 | v1.3 | 2,611,800 – 4,378,761 | `genesis-2.json` |
| 4 | v1.4 | 4,378,762 – 5,480,872 | `genesis-3.json` |
| 5 | v2.0.x | 5,480,873 – 12,598,600 (halt) | `genesis-4.json` |
| 6 | v2.0.9 (restart) | 12,598,602 – | `genesis-5.json` = `../12598602/genesis.json` |

## What is here

- `penumbra-1-blocks-1-12598600.sqlite.zst` — the reindexer block archive, **genesis to 12,598,600, gap-free**, with all five era geneses embedded. Built by merging Penumbra Labs' `reindexer-archive-height-8535185.sqlite` with blocks 8,535,186..12,598,600 archived from Rotko's own node before the restart. This is the only input `penumbra-reindexer regen` needs besides Postgres.
- `../12598600/penumbra-1_pruned_12598600.tar.lz4` — pd state at the halt (pre-migration boundary state), `../12598602/penumbra-1_pruned_12598602_migrated.tar.lz4` — post-migration state, and `../12598602/genesis.json` — the restart genesis.
- `artifacts/` — Penumbra Labs' historical artifacts (mirrored from artifacts.petrichor.guru / plinfra, verified against `SHA256SUMS.txt`): node archives at every earlier upgrade boundary (`penumbra-node-archive-height-<h>-{pre,post}-upgrade.tar.gz`) and at intermediate heights, per-era reindexer sqlite archives, CometBFT event-db and pindexer dumps, `genesis-0..4.json`.
- `../index/` — Rotko's event index at 12,598,600 as per-database `pg_dump`s: raw CometBFT events (`cometbft-events`), `pindexer`, `explorer_backend`.

## Replaying genesis → tip with penumbra-reindexer

Build from https://github.com/penumbrafi/reindexer (Rust + Go; see its README). Then:

```sh
# 1. blocks: decompress the archive (sqlite, ~90 GB)
zstd -d penumbra-1-blocks-1-12598600.sqlite.zst -o reindexer_archive.bin

# 2. target database (CometBFT psql-indexer schema is created by the tool)
createdb penumbra_raw

# 3. regenerate every era in one go (automatic migrations at 501974 / 2611799 / 4378761 / 5480872)
penumbra-reindexer regen \
  --archive-file ./reindexer_archive.bin \
  --working-dir ./regen-working-dir \
  --database-url postgresql://localhost:5432/penumbra_raw?sslmode=disable
```

Shortcuts: `regen-step --start-height H --working-dir <a pd rocksdb dir at height H>` replays from any pd state snapshot (the working dir is a plain cnidarium/pd `rocksdb` directory), so a node archive from `artifacts/` or the 12598600 snapshot lets you start mid-chain. `--allow-existing-data` fills only missing blocks. To keep an index current after the restart, run `regen-step` with `--follow https://<cometbft rpc>` from a state at 12,598,602 (the post-migration snapshot above); the 12,598,600 → 12,598,602 boundary step for `regen` is tracked in penumbrafi/reindexer (branch `restart-12598600-era`).

Throughput reference (2026-09): ~40 blocks/s inserting over a LAN Postgres, ~70 blocks/s when skipping existing blocks; the tool issues one INSERT per event attribute, so a local unix-socket Postgres or batching is the main speed-up. Regenerated app hashes match the on-chain headers (state root after 12,598,600 = `6fd4f811f8e1fcc2c67d7ea2ccc75cef228acc2b9a738b8514c9e163c5cd859a`).

Keep your own checkpoints: a ZFS snapshot of `pd/rocksdb` every ~1M blocks turns any future rebuild into hours instead of days.
