A monitor
This guide shows how to follow a log you do not trust and detect forks and rollbacks; read it when you depend on a log that someone else runs.
Example: examples/monitor
A log’s promises (append-only, one history for everyone) are only worth what is checked. A monitor
checks, at every new checkpoint, that the log’s key signed it and that the new tree contains the last
one it verified. LogStateTracker from webtessera/client (a port of Tessera’s) does the proving;
what a monitor adds is memory and alarms.
const log = newHTTPFetcher(new URL(logURL));
const verifier = newVerifier(logVkey);
const consensus = unilateralConsensus((s) => log.readCheckpoint(s));
const saved = await state.load(); // last verified checkpoint
const first = saved ?? (await consensus(verifier, verifier.name())).raw; // trust on first use
const tracker = await newLogStateTracker((l, i, p, s) => log.readTile(l, i, p, s), first, verifier, verifier.name(), consensus);
try {
const { old, newer } = await tracker.update(); // throws ErrInconsistency on a fork
if (newer !== old && newer !== undefined) await state.save(newer);
} catch (err) {
if (err instanceof ErrInconsistency) alarm(err.smallerRaw, err.largerRaw, err.proof); // the evidence
}
What to get right
- Keep the raw checkpoint, durably. The tracker keeps it to itself; take it from
update()’s result, and write it atomically (temporary file, flush, rename). A monitor that forgets its checkpoint trusts whatever the log shows next. - Keep the evidence.
ErrInconsistencycarries both checkpoints, each signed by the log, and the proof that failed: anyone with the vkey can check that the log signed two histories. - Tell a rollback from a fork. A smaller checkpoint consistent with the tracked one is an older view (often a stale cache); the tracker verifies it and keeps its own. Report it, do not adopt it.
- Know the limit. One monitor proves the log never showed it two histories. A fork that only diverges after the last checkpoint it verified is, to it, the log’s next state; catching split views takes several parties comparing checkpoints, or witnesses.
The example’s tests point the monitor at a deliberately forking fake log (two histories signed by
one key) and check a fork of the same size, a fork that grew past what was verified, a fork made
while the monitor was down, a rollback, and a checkpoint signed by someone else. The monitor needs
only fetch and node:fs, so it runs unchanged on Node, Bun and Deno.
This page is generated from docs/guides/monitor.md
. Edit it on GitHub.