Archive Replayer
mina-replayer replays all transactions from a Mina archive database, applying them sequentially to reconstruct the ledger state. It is an ongoing verification tool: run it at any time to confirm that your archive database faithfully represents the canonical chain. It also plays a role at hard-fork time — see Hard Fork Replay at the end of this page.
What the Replayer Does
The replayer reads transactions from the archive database in order (respecting global slot and sequence number) and applies them to a starting ledger. At each step, it verifies that the computed Merkle root matches what the archive recorded. This catches data corruption, missing transactions, or schema issues in your archive.
Prerequisites
- A PostgreSQL database with Mina archive data
- The
mina-replayerbinary (ships with the Mina archive Debian package and the archive/daemon Docker images) - An input JSON file specifying the starting ledger
Getting the binary
mina-replayer ships with the Mina archive Debian package and the archive/daemon Docker images — there is no need to build it from source:
- Debian: install the archive package, which puts
mina-replayeron yourPATH. - Docker: run it straight from the archive image, for example:
docker run --rm minaprotocol/mina-archive:<version>-bullseye-mainnet mina-replayer --help
Basic Usage
The replayer requires two arguments: an input file and a database connection.
mina-replayer \
--archive-uri postgres://<user>:<password>@<host>:<port>/<db> \
--input-file input.json
Required Flags
| Flag | Description |
|---|---|
--archive-uri | PostgreSQL connection string for the archive database |
--input-file | JSON file specifying the starting ledger and target state |
Optional Flags
| Flag | Description |
|---|---|
--output-file | Write the final ledger state to this file |
--continue-on-error | Don't stop on transaction application errors |
--checkpoint-interval | Create intermediate checkpoint files every N blocks |
--checkpoint-output-folder | Directory for checkpoint files |
--checkpoint-file-prefix | Filename prefix for checkpoint files |
--genesis-ledger-dir | Directory containing the genesis ledger |
--log-json | Output logs in JSON format |
--log-level | Console log level (e.g., info, debug, spam) |
--log-file | Write logs to a file |
Input File Format
The input file tells the replayer where to start. For a full replay from genesis:
{
"genesis_ledger": {
"add_genesis_winner": false,
"s3_data_hash": "<hash of genesis ledger tarball>",
"hash": "<genesis ledger Merkle root hash>"
}
}
If resuming from a previous checkpoint, include start_slot_since_genesis and any prior epoch data.
Replay Modes
Full Replay (from genesis)
This is the most thorough verification. It replays every transaction from genesis, catching any inconsistency in the entire archive history.
mina-replayer \
--archive-uri <uri> \
--input-file genesis-input.json \
--output-file output.json
Use this mode when: you want full confidence that your archive is correct.
Replay from Checkpoint
If you already have a checkpoint, you can replay just the portion after it instead of re-replaying the entire history.
mina-replayer \
--archive-uri <uri> \
--input-file checkpoint.json
Use this mode when: you want to verify recent blocks without re-replaying the full history.
Using Checkpoints for Long Replays
For mainnet, a full replay from genesis can take a long time. Use checkpoints to break it into resumable segments:
mina-replayer \
--archive-uri <uri> \
--input-file input.json \
--checkpoint-interval 10000 \
--checkpoint-output-folder ./checkpoints \
--checkpoint-file-prefix mainnet-replay
This creates a checkpoint file every 10,000 blocks. If the replay is interrupted, restart from the latest checkpoint by using it as the --input-file.
Troubleshooting
Merkle root mismatch
The replayer verifies the computed ledger Merkle root against the archive at each block. A mismatch means your archive has missing or incorrect data. Check for:
- Missing blocks (
mina-missing-blocks-auditor) - Database corruption
- Incomplete schema upgrade
Continue on error
If you want to see all errors rather than stopping at the first one:
mina-replayer --continue-on-error --archive-uri <uri> --input-file input.json
Hard Fork Replay
This section applies only while a hard fork is in progress; on a normal day you do not need it.
At a hard fork, the replayer does additional work: it replays the archive up to the fork point and exports the final ledger state as the genesis ledger for the new chain. Specifically, it:
- Replays all transactions from genesis (or a checkpoint) up to the fork point
- Stops at the
slot_chain_end(the stop-network-slot where the old chain halts) - Exports the final ledger state as a JSON checkpoint — the genesis ledger for the new chain
- Lets you compare that checkpoint against the official fork config to verify your archive matches the canonical state
Hard fork flags
| Flag | Description |
|---|---|
--hard-fork-target <fork> | The target hard fork (e.g. mesa) |
--stop-slot-config-file | JSON file with the fork parameters (stop slots, epoch data) |
--hard-fork-output-file | Output file for the post-fork genesis ledger checkpoint |
Stop-slot configuration file
The stop-slot config defines the fork parameters. This file uses the same format as the daemon runtime config:
{
"genesis": {
"genesis_state_timestamp": "2026-02-24T19:30:00Z"
},
"ledger": {
"add_genesis_winner": false,
"s3_data_hash": "<hash>",
"hash": "<ledger hash>"
},
"daemon": {
"slot_tx_end": 1900,
"slot_chain_end": 1920,
"hard_fork_genesis_slot_delta": 40
},
"epoch_data": {
"staking": {
"seed": "<vrf seed>",
"s3_data_hash": "<hash>",
"hash": "<ledger hash>"
},
"next": {
"seed": "<vrf seed>",
"s3_data_hash": "<hash>",
"hash": "<ledger hash>"
}
}
}
Key fields in daemon:
slot_tx_end— the stop-transaction-slot (no more transactions accepted after this slot)slot_chain_end— the stop-network-slot (chain halts here, this is the fork point)hard_fork_genesis_slot_delta— slot offset for the new genesis relative to the fork point
Running the hard fork replay
mina-replayer \
--archive-uri postgres://<user>:<password>@<host>:<port>/<db> \
--input-file input.json \
--hard-fork-target <fork> \
--stop-slot-config-file stop-slot-config.json \
--hard-fork-output-file output.json \
--log-json \
--log-level info
The replayer will:
- Start from the genesis ledger (or checkpoint) specified in
input.json - Replay all transactions from the archive (user commands, internal commands, and zkApp transactions)
- Stop at the
slot_chain_end— blocks at or beyond this slot are excluded - Apply the hard fork migration to produce the post-fork ledger
- Write the result to
output.json
Output format
The output file contains the genesis configuration for the new chain:
{
"start_slot_since_genesis": 1960,
"genesis_ledger": {
"hash": "<post-fork ledger Merkle root>",
"s3_data_hash": "<hash of ledger tarball>",
"add_genesis_winner": false
}
}
The start_slot_since_genesis is the new genesis slot, computed from the fork point plus hard_fork_genesis_slot_delta.
Verifying against the official fork config
After producing the replayer output, compare it to the official fork configuration published with the release:
# Compare ledger hashes (ignoring s3_data_hash, which may differ due to non-deterministic RocksDB metadata)
diff \
<(jq -S 'del(.genesis_ledger.s3_data_hash)' output.json) \
<(jq -S 'del(.genesis_ledger.s3_data_hash)' official-fork-config.json)
If the diff is empty, your archive database correctly represents the canonical chain state at the fork point.
The s3_data_hash is a SHA3-256 hash of the gzipped RocksDB ledger directory. RocksDB includes non-deterministic metadata (timestamps, sequence numbers, compaction state) and gzip headers may also differ across runs. The logical ledger contents are identical even when this hash differs — the hash field (the Merkle root) is the authoritative check.
Hard fork troubleshooting
"No blocks found before slot_chain_end" — the replayer could not find any blocks in the archive before the fork point. Verify:
- Your archive database contains blocks up to the fork slot
- The
slot_chain_endin your stop-slot config is correct
Further Reading
- Archive Node — running and maintaining an archive node
- Replayer source code