Audit log (security events)
A tamper-resistant history of sensitive actions that answers who did what, to which resource, when, and whether it worked.
See it
What it is
A security audit log is an append-only timeline of important actions: who acted, what they did, which account or resource they touched, when it happened, whether it succeeded, and enough request context to trace the event. Its records stay immutable while retained, so an attacker or administrator cannot quietly rewrite the history after the fact.
Reach for it anywhere a sensitive action needs an answer later: role changes, secret access, policy edits, exports, impersonation, and administrative overrides. GitHub's organization audit log is a useful anchor: stable event names such as `repo.destroy` plus an actor, operation, target, and timestamp make the history searchable during an incident instead of leaving responders to reconstruct it from prose logs.
The gotcha is trusting the same boundary that might be compromised. An INSERT-only table helps against application bugs, but a database administrator can still alter it. Send a second copy to separate write-once-read-many (WORM) storage such as Amazon S3 Object Lock, restrict readers with least privilege, and never record passwords, session cookies, API tokens, or full request bodies. Immutability does not mean keeping personal data forever, so set and enforce a documented retention window.
Ask AI for it
Build a security audit log with an append-only PostgreSQL audit_events table containing event_id, event_type, actor_id, target_type, target_id, outcome, request_id, ip_address, metadata jsonb, and an RFC 3339 created_at timestamp. Define stable event names for role changes, policy edits, secret access, data exports, impersonation, and admin overrides. Write each event server-side in the same transaction as the protected action, grant the application role INSERT and SELECT but no UPDATE or DELETE, and redact keys matching password, cookie, authorization, secret, and token before serialization. Stream a second copy to a versioning-enabled Amazon S3 bucket with Object Lock in Compliance mode, using an explicit retention window of 400 days as the bucket default, and give that writer s3:PutObject without s3:DeleteObject or s3:DeleteObjectVersion. Index actor_id plus created_at and target_type plus target_id plus created_at, and expire rows past the window with a pg_cron job that runs as a separate privileged role.