Capabilities/Security & Payments

Audit Logging

Append-only event stores, tamper-proof records, and compliance-ready activity trails

auditloggingcompliancesecurityimmutable

Learning lens

Start with judgment, not a universal prescription.

This page is a learning model for understanding the capability. It does not claim exact production internals, exact costs, or an infinite-scale architecture.

Evidence

Conceptual pattern

0->1 default

Start with explicit trust boundaries, auditable decisions, and managed primitives where correctness matters more than novelty.

Cost drivers

audit retentionencryption and key managementpolicy checkscompliance evidence

Watch for breakpoints

  • -authorization logic spreads across services
  • -audit queries become operationally important
  • -customers require stronger controls

Learn first

Concepts behind this architecture

The Problem

Regulated industries (healthcare, finance, government) and security-conscious applications need a complete, tamper-proof record of who did what, when, and from where. Audit logs answer questions like “Who accessed this patient record?”, “Who changed the billing amount?”, and “What admin actions were taken during the incident?” Unlike application logs (for debugging), audit logs are legal records that may be subpoenaed. They must be immutable, complete, queryable, and retained for years — while adding minimal latency to the operations being audited.

Architectural Approaches

Application-Level Audit Middleware

Intercept operations at the application layer and emit structured audit events. Middleware on API routes captures the actor, action, resource, and outcome. Events are written to a dedicated audit log store asynchronously to avoid impacting request latency.

Pros

  • +Rich context — captures business-level semantics (not just SQL)
  • +Can include request context (IP, user agent, session)
  • +Flexible — audit only what matters, skip noisy operations
  • +Works with any database and framework

Cons

  • −Must be added to every relevant code path — easy to miss
  • −Application bugs can cause missing or incorrect audit entries
  • −Developers must remember to add audit logging to new features
  • −Bypassed by direct database access

Database-Level Audit Triggers

Use database triggers or change data capture (CDC) to automatically record every INSERT, UPDATE, and DELETE in an audit table. Captures all changes regardless of which application or user made them.

Pros

  • +Captures ALL data changes — nothing can be missed
  • +Cannot be bypassed by application code
  • +Automatic — no developer action needed for new tables
  • +Includes before/after values for every change

Cons

  • −No application context (who initiated the change, from which UI)
  • −High volume — every row change generates an audit entry
  • −Performance impact on write-heavy tables
  • −Database-specific implementation (Postgres triggers differ from MySQL)

Event Sourcing as Audit Log

If your system uses event sourcing, the event store IS the audit log. Every state change is recorded as an immutable event. The current state is derived by replaying events. The complete history is preserved by design.

Pros

  • +Audit logging is built into the architecture — not an afterthought
  • +Complete history with perfect fidelity
  • +Can reconstruct state at any point in time
  • +Events are immutable by design

Cons

  • −Event sourcing is a major architectural commitment
  • −Not practical to retrofit into existing CRUD applications
  • −Event store can grow very large over time
  • −Querying historical state requires event replay or projections

Architecture

Loading diagram...
Audit Logging — High-level architecture

Key Components

Audit Middleware

Intercepts API requests and emits structured audit events. Captures: actor (user ID, IP, session), action (HTTP method, business operation), resource (type, ID), outcome (success/failure, status code), and timestamp. Emits events asynchronously to avoid blocking the request.

Audit Processor

Consumes audit events from the queue, enriches them with additional context (actor name, resource details), validates the event schema, and writes to the audit store. Handles deduplication and ordering. Runs as a separate service for isolation.

Integrity Signer

Generates a cryptographic hash chain for tamper detection. Each audit entry includes a hash of itself plus the previous entry’s hash, forming an append-only chain. Any modification to a historical entry breaks the chain. Similar to a simplified blockchain.

Hot Audit Store

Queryable database for recent audit logs (last 90 days). PostgreSQL with proper indexing (actor, resource, timestamp) or Elasticsearch for full-text search across audit entries. Optimized for fast queries and filtering.

Cold Archive

Long-term storage for audit logs beyond the hot window. S3 with lifecycle policies moving to Glacier/Deep Archive after 1 year. Stored as compressed, encrypted Parquet or JSON files. Retention period set by compliance requirements (7 years for SOX, 6 years for HIPAA).

Audit Query API & Dashboard

Provides filtered, paginated access to audit logs. Supports queries by actor, resource, action, time range, and outcome. Dashboard for security teams to investigate incidents. Export functionality for compliance auditors (CSV, PDF reports).

Data Model

Loading diagram...
Audit Logging — Entity relationship diagram

Tamper-Proof Hash Chain

Audit logs must be tamper-proof — if someone modifies a historical entry, it should be detectable.

Hash chain implementation:

  1. For each audit event, compute: hash = SHA-256(event_id + actor + action + resource + timestamp + previous_hash)
  2. Store the hash alongside the event
  3. The first event in the chain uses a known seed value for previous_hash
  4. To verify integrity: recompute hashes from the beginning and compare with stored hashes

Verification: Run a daily integrity check job that walks the entire chain and verifies each hash. Any break in the chain indicates tampering. Alert the security team immediately.

Anchoring: Periodically publish the latest hash to an external system (a public blockchain, a separate write-once storage, or a third-party timestamping service). This prevents an attacker who gains database access from rewriting the entire chain.

Performance: SHA-256 hashing is fast (~1 microsecond per entry). The chain verification job processes millions of entries per minute. Run incrementally — only verify entries since the last check.

Alternative: Use a Merkle tree instead of a linear chain. Allows efficient proof that a specific entry exists in the log without verifying the entire chain. More complex but better for selective verification.

What to Audit

Not every operation needs auditing. Focus on security-relevant and compliance-required events.

Always audit:

  • Authentication events: login, logout, failed login attempts, password changes, MFA enrollment
  • Authorization changes: role assignments, permission grants/revocations
  • Data access: reads of sensitive data (PII, financial records, health data)
  • Data modifications: creates, updates, deletes of business-critical records
  • Administrative actions: user management, configuration changes, feature flag toggles
  • API key and token management: creation, rotation, revocation

Consider auditing:

  • Search queries on sensitive data (who searched for patient records?)
  • Export operations (who downloaded the customer list?)
  • Impersonation/admin access (admin viewed user’s account)
  • Billing and payment operations

Skip (use application logs instead):

  • Read-only access to non-sensitive data
  • Health checks and monitoring requests
  • Static asset requests
  • Internal service-to-service communication (unless accessing sensitive data)

Structured format: Every audit entry should include: WHO (actor ID, actor type, IP, session), WHAT (action, resource type, resource ID), WHEN (timestamp, timezone), WHERE (IP, user agent, geolocation), and OUTCOME (success/failure, error details).

Retention and Compliance

Different regulations have different retention requirements:

  • SOX (Sarbanes-Oxley): 7 years for financial audit trails
  • HIPAA: 6 years for access logs to protected health information
  • GDPR: Retain only as long as necessary (conflict with long retention!)
  • PCI DSS: 1 year minimum, 3 months immediately accessible
  • SOC 2: No specific retention, but auditors expect 12+ months

GDPR tension: GDPR requires data minimization — don’t keep data longer than necessary. But audit logs may contain PII (user names, emails, IP addresses). Solutions:

  • Anonymize/pseudonymize PII in audit logs after the active period
  • Keep audit logs but redact PII fields after the retention window
  • Document the legal basis for retaining audit logs (legitimate interest, legal obligation)

Tiered storage:

  • Hot (0-90 days): Fast queryable store (PostgreSQL, Elasticsearch). Full detail.
  • Warm (90 days - 1 year): Compressed storage (S3 Standard). Queryable with some delay.
  • Cold (1-7+ years): Archive storage (S3 Glacier). Retrieved on demand for audits.

Immutability enforcement: Use write-once storage (S3 Object Lock, WORM-compliant storage) for cold archives. Even database administrators cannot modify or delete archived audit logs. This is required for SOX compliance.

Real-World Examples

GitHub

Enterprise audit log capturing all organization-level actions: repository access, member management, SSO events, and API access. Streamable to external SIEM systems via audit log streaming. Searchable via API with filters for actor, action, and time range.

AWS CloudTrail

Records every API call made in an AWS account. Captures who (IAM principal), what (API action), which resource, and when. Logs stored in S3 with optional CloudWatch integration. Supports multi-region and organization-wide trails.

Datadog

Audit Trail feature tracks all configuration changes, user actions, and API access within the Datadog platform. Every dashboard edit, monitor change, and access key creation is logged with full actor context. 90-day retention with export capability.

Stripe

Complete audit trail of all API requests and dashboard actions. Every payment, refund, and configuration change is logged with the actor (API key or dashboard user), timestamp, and request details. Accessible via the dashboard and Events API.

Architectural Tradeoffs

Application-level vs database-level auditing

Pros

  • +Application: rich business context, selective auditing
  • +Database: captures ALL changes, cannot be bypassed
  • +Both: belt-and-suspenders approach for critical systems

Cons

  • −Application: can miss changes from direct DB access or other apps
  • −Database: noisy, no business context, high volume
  • −Both: more complexity, potential duplicate entries

Synchronous vs asynchronous audit logging

Pros

  • +Synchronous: guaranteed — if the operation succeeds, the audit entry exists
  • +Asynchronous: no latency impact on the operation
  • +Asynchronous: can batch writes for better throughput

Cons

  • −Synchronous: adds latency to every audited operation
  • −Asynchronous: small window where operation succeeds but audit entry is lost
  • −Asynchronous: must handle queue failures and ensure delivery

Relational DB vs append-only log (Kafka) for audit storage

Pros

  • +Relational: easy querying with SQL, familiar tooling
  • +Kafka: naturally append-only, immutable, high throughput
  • +Relational: better for complex queries (joins, aggregations)

Cons

  • −Relational: rows can be updated/deleted (must restrict access)
  • −Kafka: harder to query (need to materialize into a queryable store)
  • −Kafka: retention management is partition-based, less granular

Learn Audit Logging

Copy a learning context prompt for an AI assistant. It keeps assumptions visible and asks for tradeoffs, cost drivers, failure modes, and what to measure before adding complexity.

Explore More Capabilities