Tamper-evident audit log with a hash chain

Backend Live Coding November 13, 2026 python

The hash chain (each entry contains the hash of the previous one) shows whether someone modified or deleted an entry.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ======================================================================
# Q: Write an audit log that cannot easily be tampered with.
#
# The hash chain (each entry contains the hash of the previous one) shows
# whether someone modified or deleted an entry. For strict compliance, keep a
# copy in WORM storage (e.g. S3 Object Lock). Entries are written from a single
# place, in order, so the chain does not break.
# ======================================================================

class AuditLog(Base):
    __tablename__ = 'audit_log'
    id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
    at: Mapped[datetime] = mapped_column(server_default=func.now())
    tenant_id: Mapped[int]
    actor_id: Mapped[int | None]
    action: Mapped[str]                       # 'reports:export'
    target: Mapped[str]                       # 'report:123'
    ip: Mapped[str | None]
    details: Mapped[dict] = mapped_column(JSONB, default=dict)
    prev_hash: Mapped[str]
    hash: Mapped[str]                         # sha256(prev_hash + entry)

-- In the database: the application can only write
REVOKE UPDATE, DELETE ON audit_log FROM app_user;