How this works
What is left out, what is checked, how often — and what we cannot promise.
Two layers, one snapshot
A 0G node is two programs. geth is the execution layer: accounts, contracts, EVM blocks. 0gchaind is the consensus layer, built on CometBFT: it orders blocks and hands each block's execution payload to geth over the Engine API. Block H of 0gchaind carries EVM block H.
The two must stand at the same height. If geth is behind, 0gchaind asks it to accept a block whose parent it does not have, geth answers INVALID, and the node cannot start. That is why the snapshot is always taken from both at the same moment, with both stopped.
Why it is smaller
The old snapshot was the two data directories as tarballs, about 125 GB. Most of the consensus side was CometBFT block history — every block since our node started, about 130 GB on disk — which a new node does not need: it follows the chain from the height it starts at. So the blockstore is rebuilt with a single block, the last one, taken from our node's own blockstore and checked against its stored block ID. The transaction index, which the node no longer writes, is left out too.
geth's data ships whole: the state, and the block history geth keeps in its freezer, which geth needs to serve its own chain. Everything is compressed with zstd and split into parts.
Why you can trust it
- Before publishing, the hash of CometBFT block H must equal what a public CometBFT RPC serves for that height.
- The hash of geth's head block must equal what independent public EVM RPCs serve for that number — at least two of them, and none may disagree.
- geth's freezer files grow while the next parts are built; the last megabyte of each one is hashed at capture and again after packing, and any change refuses the publish.
- On your machine every part is checked by sha256 while it downloads.
- On start 0gchaind replays the last block through geth and checks its own app hash; geth checks the block's state root. A wrong state stops the node at the first block.
How it is made, every half hour
The node is stopped for seconds, cleanly. 0gchaind first, then geth with SIGINT so it writes the state it keeps in memory to disk. Then a copy is made without copying: the database files that never change after they are written — pebble SST files, geth freezer files — are hard-linked, and only the few files the node rewrites in place (manifests, write-ahead logs, freezer metadata) are copied. geth and 0gchaind start again; the downtime is printed in the log and in every manifest.
Then the parts are built while the node runs on. Parts are named by their sha256, so a part that did not change is never stored twice. Unchanged SST files travel in the same groups from one snapshot to the next, and the freezer files only grow, so their beginnings are reused as is: each cycle packs mostly what the chain added since the last one. The manifest is swapped only after every part is in place.
A copy of a running geth is not possible: it keeps recent state in memory, so a copy taken under it is behind 0gchaind — the same INVALID as above. The stop is the price of a snapshot that starts.
What we cannot promise
The snapshot is as old as the last capture plus its build: up to about half an hour. After it your node catches up through blocksync, and that is slower than the chain is fast — several blocks a second on our server, so plan minutes per half hour of age. Network upgrades need the matching release; the manifest always names the versions the data was written with. Latencies on the Endpoints page are from our server, not from yours.