Most Laravel applications record important events:
- user logins
- orders created
- payments processed
- settings changed
These events are usually stored in an activity log table.
Something like this:
id | action | user_id | created_at
At first glance, this seems fine.
But there’s a serious problem.
Audit Logs Can Be Modified
In most applications, audit logs are just database records.
That means they can usually be:
- modified
- deleted
- reordered
If someone with database access changes a record, there’s often no reliable way to detect it.
For many systems this is unacceptable.
Especially for:
- financial systems
- healthcare platforms
- security-sensitive applications
- compliance reporting
These systems need tamper-detectable audit trails.
The Idea: Turn Logs into a Ledger
Instead of recording events as simple database records, I built a system that records them in a cryptographically verifiable ledger.
I called it Chronicle.
Chronicle is an append-only audit ledger for Laravel.
Each entry is linked to the previous one using a hash chain.
If any entry is modified or removed, the ledger verification fails.
Recording an Event
Using Chronicle looks like this:
use Chronicle\Facades\Chronicle;
Chronicle::record()
->actor($user)
->action('order.created')
->subject($order)
->metadata([
'amount' => 5000,
'currency' => 'USD',
])
->commit();
Each entry records:
- who performed the action
- what happened
- what entity was affected
Entries are immutable once recorded.
Hash Chaining
Chronicle protects the ledger using a hash chain.
Each entry references the previous entry:
chain_hash(n) = SHA256(chain_hash(n-1) + payload_hash(n))
This creates a cryptographic chain across the entire audit history.
If any entry is:
- modified
- deleted
- reordered
the chain verification fails.
Detecting Tampering
Chronicle includes a verification command:
php artisan chronicle:verify
If the ledger was tampered with, verification fails.
This allows systems to detect unauthorized changes to audit logs.
Exporting a Verifiable Dataset
Chronicle can export the ledger as a verifiable dataset.
php artisan chronicle:export
Exports include:
entries.ndjson
manifest.json
signature.json
The dataset contains:
- a dataset hash
- a digital signature
- ledger boundaries
This allows the exported audit log to be verified independently.
Querying the Ledger
Chronicle includes query helpers for retrieving entries:
use Chronicle\Models\Entry;
Entry::forActor($user);
Entry::forSubject($order);
Entry::action('order.created');
It also supports:
- cursor pagination
- streaming large ledgers
- indexed queries
This makes it practical to use in real systems.
Why I Built This
Many applications rely on audit logs to answer questions like:
What happened?
But traditional logs can’t always answer a more important question:
Can we prove what happened?
Chronicle attempts to solve that problem.
Instead of storing events as editable records, it records application history as a ledger.
Open Source
Chronicle is open source and available on GitHub:
https://github.com/laravel-chronicle/core
If the idea sounds interesting, feel free to check it out.
Feedback and contributions are welcome.
Top comments (0)