Auditing the Event Store
This guide explains how to audit the integrity of events stored in EventSourcingDB. Using the audit endpoint, you can verify that the event chain has remained unchanged, ensuring tamper-proof compliance and reliable historical data integrity.
How Event Chain Auditing Works#
EventSourcingDB stores events in an immutable, append-only log. Each event contains a cryptographic hash referencing its predecessor, forming a secure, verifiable chain similar to a blockchain.
When performing an audit, EventSourcingDB:
- Calculates the cryptographic hash for each event.
- Verifies that each event's hash matches the predecessor hash stored in the subsequent event.
- If provided, checks that a previously audited event's ID and hash match exactly, ensuring that no past events were tampered.
This mechanism enables external auditors to regularly confirm the integrity of stored events, reducing the audit scope to newly added events since the last verification.
For detailed information on event storage and consistency, see Consistency Guarantees.
Performing an Audit#
Changed in 1.3To audit the event store, send an HTTP POST request to /api/v2/management/audit-history. Its body names, as lastAuditedEvent, the event the previous audit ended at, which anchors the audit.
Initial Audit#
When performing an audit for the first time, set lastAuditedEvent to null:
curl \
-i \
-X POST \
-H "Authorization: Bearer <API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "lastAuditedEvent": null }' \
http://localhost:3000/api/v2/management/audit-history
lastAuditedEvent is required: a body that leaves it out, such as {}, is answered with last-audited-event-missing rather than taken for a first audit, so that an audit that has lost its anchor does not skip checking it.
Subsequent Audits#
If you previously audited the store, send the id and hash of the event the last successful audit ended at, exactly as it answered them. EventSourcingDB will verify the integrity starting from the first event up to and including the given event, check that this event still has the given hash, and continue with all subsequent events:
curl \
-i \
-X POST \
-H "Authorization: Bearer <API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"lastAuditedEvent": {
"id": "41",
"hash": "esdb:hash:v2:fb3a4d1324f3b00865d15eb89ab678e2f23b8fe8eda8df611b05bbae10428130"
}
}' \
http://localhost:3000/api/v2/management/audit-history
Interpreting Audit Results#
Changed in 1.3The server responds with a stream of newline-delimited JSON. While the audit is still running, the server sends heartbeat items to keep the connection open.
If the audit succeeds, the stream ends with a lastAuditedEvent item, which names the latest event audited by its id and hash, confirming the integrity of the entire event chain up to this point, followed by the end item. Keep the lastAuditedEvent: the next audit sends it as its anchor.
{"type":"lastAuditedEvent","data":{"id":"41","hash":"esdb:hash:v2:fb3a4d1324f3b00865d15eb89ab678e2f23b8fe8eda8df611b05bbae10428130"}}
{"type":"end","data":{}}
Changed in 1.3The audit covers the events up to the latest one at its start. Writes go on while it runs, and the events they add are left for the next audit, which you start from this result.
Changed in 1.3If the store holds no events, the stream holds no lastAuditedEvent item, only the end item, and the next audit sends null again.
If the audit finds a problem, the stream ends with an error item instead, followed by the end item. Its code says what the audit found:
event-chain-broken: an event does not follow on the one before it, or does not match its own hash, so the event chain has been tampered with or damaged. Itsdetailsname the event at which the chain breaks.last-audited-event-mismatched: the event thatlastAuditedEventnames has another hash in the database, so the events have changed since the previous audit, or the anchor does not come unchanged from this database.last-audited-event-unknown: the database holds no event with the ID thatlastAuditedEventnames, for example because the anchor comes from another database.event-hash-version-unknown: an event carries a hash of a version that this version of EventSourcingDB does not know, so the audit cannot check it. Itsdetailsname the event.file-unreadable: a file of the store is damaged, so the audit cannot read on. Itsdetailsname the file; verify the store, and restore a backup.
{"type":"error","data":{"code":"https://docs.eventsourcingdb.io/errors/event-chain-broken","description":"event chain is broken: an event does not match its hash","details":{"eventId":"17"}}}
{"type":"end","data":{}}
The stream also ends with an error item if the audit itself cannot run to completion: server-shutting-down if the database shuts down meanwhile, and store-failed if it refuses reads for good, which it only does after a restore failed and could not empty the database again. If the audit fails unexpectedly otherwise, say because reading from the disk fails, the stream breaks off without its end item. A restore does not end an audit, nor is an audit refused while a restore runs: the audit waits until the restore is over and then goes on. As long as the database holds events, a restore is turned down anyway. The status code is already 200 by the time the first item goes out, so the end of the stream is what tells you whether the audit succeeded: it did if the stream ends with end and holds no error item. Treat anything else as a failed audit, not as a clean one.
If the audit found a mismatch or a tampered chain, further investigation is required. Review logs, check the last known good state, and consider running a full store verification as described in Verifying Store Integrity.
Typical Use Cases#
Auditing the event store is crucial whenever strong guarantees about historical integrity are required. Typical scenarios include external compliance audits, where auditors must independently verify that no historical data was tampered. Regular audits are also useful in internal governance processes to maintain trustworthiness of data over time.
In industries like finance, healthcare, or public services, audit trails are often a legal requirement, and the ability to efficiently validate the event history is essential. Additionally, audits provide a valuable tool in dispute resolution, enabling teams to prove that critical business events have not been manipulated after the fact.
Best Practices#
Changed in 1.3It is recommended to perform audits regularly and securely archive the lastAuditedEvent returned from each successful audit. Each audit then confirms that nothing has changed up to the previous one, so a review only has to look at the newly added events.
Automating the audit process, for example as part of scheduled maintenance routines, helps ensure consistent verification without manual overhead.
Whenever an audit fails, immediate investigation should be triggered, as any detected tampering could indicate a serious security incident or systemic issue. For comprehensive data integrity management, it is advisable to combine regular auditing with full store verifications as described in Verifying Store Integrity.
Auditing provides a lightweight but powerful mechanism to protect the long-term trustworthiness of event-sourced data and to demonstrate compliance with security, legal, and operational standards.