Skip to main content

Scalable Verification

Full verification re-hashes every entry from genesis — O(entries) in ledger size. On large ledgers that is expensive to run often. Chronicle v1.11 adds range-aware checkpoints and several verification modes so you can verify less while keeping strong guarantees, and verify more (external anchors) when you need them.

These modes need checkpoints with the v1.11 range columns (head_id, entry_count, previous_checkpoint_id). On a ledger upgraded from 1.10, run chronicle:checkpoints:backfill first (see the Upgrade Guide); until then, the incremental modes fall back to a full verify with a warning.

The modes

All modes are flags on chronicle:verify.

Full verify (default)

php artisan chronicle:verify

Walks every entry from genesis, checking each payload_hash and chain_hash plus linked checkpoint signatures. Cost: O(entries). Use: after a restore, during incident response, or as a periodic deep check.

--checkpoints-only

php artisan chronicle:verify --checkpoints-only

Verifies only the checkpoint chain: each signature, its previous_checkpoint_id linkage, entry_count contiguity, and that its chain_hash matches its head entry. Cost: O(checkpoints) — reads no entries. Use: a fast attestation that the signed boundaries are intact and well-linked.

--since-last-checkpoint

php artisan chronicle:verify --since-last-checkpoint

Trusts the latest checkpoint and verifies only the entries appended after it. Cost: O(tail). Use: frequent, cheap checks that new writes are well-formed.

--from-checkpoint / --to-checkpoint

php artisan chronicle:verify --from-checkpoint=<ULID> [--to-checkpoint=<ULID>]

Verifies a bounded segment between two checkpoints (to the current head if --to-checkpoint is omitted), seeded from the trusted starting checkpoint. Cost: O(segment). Use: verify a single epoch — e.g. the window around a key rotation — without re-reading the whole ledger.

--resume

php artisan chronicle:verify --resume

Continues from the last recorded verification run, verifying only the new tail (full verify if there is no prior run). Cost: O(new tail). Use: scheduled verification that picks up where the last run left off. Falls back to a full verify (with a warning) if the progress table is absent.

--anchors

php artisan chronicle:verify --checkpoints-only --anchors

Adds an external-anchor pass over the checkpoints in scope: each must carry at least one valid anchor (see External Anchoring). Composes with --checkpoints-only and the incremental modes. Cost: O(checkpoints in scope) plus one provider verification each — offline for RFC 3161, one S3 read for the S3 adapter. Use: prove the ledger against tampering that re-signs internally but cannot forge the external anchor.

Choosing a mode

GoalMode
Deep periodic / post-restorefull
"Are the boundaries intact?" (fast)--checkpoints-only
"Are recent writes valid?" (cheap)--since-last-checkpoint
Verify one epoch / rotation window--from-checkpoint + --to-checkpoint
Scheduled, incremental--resume
Defeat a full internal compromise--anchors

Every mode agrees with full verify on the entries it covers — they trade scope for cost, not rigor.

New failure reasons

In addition to the entry-level reasons (payload_hash_mismatch, chain_hash_mismatch, checkpoint_missing, checkpoint_signature_invalid, unknown_key), the v1.11 modes can report:

ReasonMeaning
checkpoint_chain_brokenA checkpoint's previous_checkpoint_id linkage is missing or wrong
checkpoint_head_mismatchA checkpoint's chain_hash does not match its head entry
segment_discontinuousA verified segment does not join its starting checkpoint
anchor_invalidA checkpoint in scope has no valid external anchor (missing or invalid)

Checkpoint cadence

Each incremental pass is bounded by checkpoint spacing — the closer your checkpoints, the smaller each --since-last-checkpoint, segment, or --resume pass. Create checkpoints on a cadence that matches write volume and recovery objectives (e.g. a scheduled checkpoint plus one every N entries). See Schedule Checkpoints & Exports and Performance & Indexing.

Indexing for large ledgers

checkpoint_id is populated on covered entries when a checkpoint is created (v1.11), and the migration indexes it. See Performance & Indexing for the checkpoint_id and checkpoint-chain indexes.

See also