Courses/Audit Log + Hash Chain
Standalone Course~30 min4 challenges

Audit Log + Hash Chain

Every Grit project records who did what — automatically. Each authenticated mutation is appended to a SHA-256 hash chain that makes the log tamper-evident: change one row in the database and the chain no longer verifies. This is the audit trail auditors actually trust.

Authenticated actionAudit logVerificationappendUser actioncreate · update · deleteLog entryhash(prev + this)Hash chaineach links the lastTamper checkedit one → breaks
Hash-chainedTamper-evident
Every action is hash-chained to the previous one — change a row and the chain stops verifying

Reference docs: Security Guide →

Why audit logs matter

Compliance, forensics, and accountability — who changed what, when, and from where.

How the hash chain works

Each entry seals the one before it, so any edit or deletion breaks the chain.

Reading the trail

Filter activity by user, action, and resource straight from the admin panel.

Proving integrity

Verify the entire chain with a single GET request and surface tampering instantly.

What is an audit log?

An audit log is an append-only record of meaningful changes in your system. Unlike application logs (which are for debugging), an audit log is for accountability — it answers “who created this invoice,” “who deleted that user,” and “when did the price change.”

Tamper-Evident: A record where any unauthorized change is detectable. The data isn't necessarily un-editable (someone with database access can still run SQL), but if they do, you can prove it was altered. That proof is what a hash chain provides.

In Grit, audit logging is automatic. A middleware records every authenticated mutating request (POST, PUT, PATCH, DELETE) into an activity_logs table — no per-handler code required.

How the hash chain works

Each log entry stores a hash computed from the entry's contents plus the previous entry's hash. That links every row to the one before it, like blocks in a blockchain. Recompute the chain and a single altered row invalidates every hash after it.

Hash Chain: A sequence where each element contains a cryptographic hash of the previous element. hash(n) = SHA256(hash(n-1) + payload(n)). Because hashes are one-way and collision-resistant, you can't edit a past entry without redoing every hash that follows — which the verifier will catch.
conceptual — how each entry is sealed
// Pseudocode for the chain seal Grit computes per entry
entry.PrevHash = lastEntry.Hash
payload := entry.UserID + entry.Action + entry.Resource +
           entry.ResourceID + entry.Timestamp + entry.PrevHash
entry.Hash = sha256(payload)   // stored on the row
The chain proves integrity, not secrecy. Audit entries are still readable by admins — the point is that they can't be silently rewritten.

Reading the trail

The admin panel exposes the activity log with filters for user, action, and resource. Each row captures the actor, the HTTP method + path, the target resource, the timestamp, and the request's public IP.

example activity entries
a3f1...c92   maya@acme.com   POST   /api/invoices       192.0.2.10
b9d2...44e   jb@grit.dev     PUT    /api/users/42       198.51.100.7
c1e7...8af   maya@acme.com   DELETE /api/blogs/9        192.0.2.10
1

Challenge: Generate some activity

Log in to the admin panel and create, update, then delete a record. Open the Activity page. Do you see three new entries? What actor, action, and resource does each show?

Proving integrity

Verification re-walks the chain from the beginning, recomputing each hash and comparing it to what's stored. If every entry matches, the log is intact. If one doesn't, you get the exact entry where the chain broke.

verify the whole chain
# Returns { "ok": true } when the chain is intact,
# or the first entry id where verification failed.
GET /admin/activity/integrity
Authorization: Bearer <admin JWT>
Run integrity verification on a schedule (Grit's cron scheduler is perfect for this) and alert if it ever returns ok: false. That turns a passive log into an active tripwire.
2

Challenge: Break the chain on purpose

Open GORM Studio at localhost:8080/studio and edit a single field on an old activity_logs row. Now call /admin/activity/integrity. Does it report the tampering? Which entry id does it flag?

3

Challenge: Schedule a daily check

Add a cron entry that hits the integrity endpoint every night. Where would you send an alert if it fails? Sketch the flow.

4

Challenge: Map it to a compliance control

Pick SOC 2, HIPAA, or PCI-DSS. Name one specific control the hash-chained audit log helps you satisfy, and explain why “tamper-evident” matters for it.