Export Verification Guide
Chronicle exports a deterministic dataset that can be verified independently of the source application.
Export layout
Every export directory contains three files:
chronicle-export/
├─ entries.ndjson
├─ manifest.json
└─ signature.json
Generate an export with:
php artisan chronicle:export storage/app/chronicle-export
Verify it with:
php artisan chronicle:verify-export storage/app/chronicle-export
What gets verified
Chronicle verifies the export in layers:
- Required files exist
manifest.jsoncan be read and decodedsignature.jsoncan be read and decodedentries.ndjsonis readable and valid line-delimited JSON- Each entry's chain hash matches the previous chain state
- Entry count and first/last entry boundaries match the manifest
- The computed dataset hash matches
manifest.json - The signature validates against the dataset hash
File roles
entries.ndjson
This is the exported ledger stream. Each line contains one entry in export order.
Chronicle exports entries ordered by:
created_atid
The verifier recomputes the dataset hash from the raw NDJSON bytes and also walks the chain hash across the exported entries.
manifest.json
The manifest records export-level metadata:
versiongenerated_atentry_countfirst_entry_idlast_entry_idchain_headdataset_hashalgorithm
For empty datasets, first_entry_id, last_entry_id, and chain_head must be null.
signature.json
The signature file stores the detached signature and related metadata:
signaturealgorithmkey_id
Chronicle signs the manifest's dataset_hash, not the entire manifest blob.
Failure modes
The verifier returns specific machine-readable failure reasons. Common ones include:
entries_missingentries_unreadableentries_invalid_jsonentries_invalid_formatmanifest_missingmanifest_unreadablemanifest_invalid_jsonmanifest_invalidsignature_missingsignature_unreadablesignature_invalid_jsonsignature_invalid_formatchain_invalidentry_count_mismatchfirst_entry_mismatchlast_entry_mismatchchain_head_mismatchdataset_hash_mismatchsignature_invalid
These failure codes are useful if you want to wrap Chronicle verification inside CI, operational checks, or downstream audit tooling.
Manual verification flow
If you are building an external verifier, the expected flow is:
- Read
manifest.jsonandsignature.json - Validate their structure
- Read
entries.ndjsonline by line - Recompute the chain from an initial previous chain value of
"0" - Track the first entry id, last entry id, and entry count
- Recompute the SHA-256 hash of the raw
entries.ndjsonfile - Compare all computed values with the manifest
- Verify the detached signature against
dataset_hash
Chronicle's default signing provider uses Ed25519.
Operational guidance
- Store exports in write-once or tightly controlled storage
- Keep public keys available to the systems performing verification
- Treat
dataset_hashandchain_headas auditable artifacts worth recording in external systems - Re-run verification after transport, backup restore, or handoff to third parties
Example workflow
php artisan chronicle:export storage/app/chronicle-export
php artisan chronicle:verify-export storage/app/chronicle-export
sha256sum storage/app/chronicle-export/entries.ndjson
The first command produces the dataset, the second validates Chronicle's invariants, and the last command lets you compare the raw file hash with manifest.json.