Audit Log

The ledger_events table in the causet database is Causet’s audit log. Every state-changing action produces a ledger event. The ledger is append-only — events are never updated or deleted through application code paths.

Note: For the observability angle on the audit log (forensics, replay, debugging), see Audit Events. This page focuses on compliance, access, and retention.


What the audit log records

Every ledger event contains:

FieldDescriptionExample
event_idUnique event identifierevt_abc123
event_typeThe event nameARTIST_FOLLOWED
entity_idThe affected entityuser-1
entity_typeThe entity typeuser
payloadFull event payload (JSONB){"user_id": "user-1", "artist_id": "artist-1"}
correlation_idLinks to the intent that caused thiscorr-xyz789
fork_idDeployment environmentproduction
platform_idPlatformmy-platform
application_idApplicationconcert-app
created_atCommitted timestamp (microseconds)2026-06-25 20:00:00.123456

Who changed what, when

The standard audit query pattern:

-- All changes to a specific entity
SELECT
  event_id,
  event_type,
  entity_id,
  payload,
  correlation_id,
  created_at
FROM causet.ledger_events
WHERE entity_id = 'user-1'
  AND platform_id = 'my-platform'
  AND application_id = 'concert-app'
  AND fork_id = 'production'
ORDER BY created_at ASC;
-- All events in a time range for a specific event type
SELECT
  event_id,
  entity_id,
  payload,
  created_at
FROM causet.ledger_events
WHERE event_type = 'ARTIST_FOLLOWED'
  AND fork_id = 'production'
  AND created_at BETWEEN '2026-06-01' AND '2026-06-30'
ORDER BY created_at DESC;
-- Trace a complete action (find all events from one intent)
SELECT
  event_type,
  entity_id,
  payload,
  created_at
FROM causet.ledger_events
WHERE correlation_id = 'corr-xyz789';

Compliance audit queries

User activity report

-- All actions performed by a user in the last 90 days
SELECT
  event_type,
  payload->>'artist_id' AS artist_id,
  created_at
FROM causet.ledger_events
WHERE entity_id = 'user-1'
  AND entity_type = 'user'
  AND fork_id = 'production'
  AND created_at >= NOW() - INTERVAL '90 days'
ORDER BY created_at DESC;

Event type frequency report

-- Event counts by type for a date range
SELECT
  event_type,
  COUNT(*) AS event_count,
  MIN(created_at) AS first_seen,
  MAX(created_at) AS last_seen
FROM causet.ledger_events
WHERE fork_id = 'production'
  AND created_at >= '2026-01-01'
GROUP BY event_type
ORDER BY event_count DESC;

Building audit projections

For compliance queries with a fixed shape (e.g., “all follow/unfollow actions by user”), create a projection table:

# projections/audit.projections.causet
projections:
  follow_audit_log:
    source_events: [ARTIST_FOLLOWED, ARTIST_UNFOLLOWED]
    target:
      table: follow_audit_log
      primary_key: [event_id]
    fields:
      event_id:    TEXT
      user_id:     TEXT
      artist_id:   TEXT
      action:      TEXT
      occurred_at: BIGINT
    derive:
      event_id:    event.event_id
      user_id:     event.user_id
      artist_id:   event.artist_id
      action:      event.event_type
      occurred_at: event.ts
    mutations:
      ARTIST_FOLLOWED:   { op: upsert }
      ARTIST_UNFOLLOWED: { op: upsert }

This materializes a purpose-built audit table that:

  • Is indexed for fast compliance queries
  • Has a specific schema for audit report generation
  • Can be queried without joining against the full ledger_events table

Query it through the query service:

queries:
  follow_audit:
    from: follow_audit_log
    where:
      user_id: { eq: input.user_id }
    order_by:
      occurred_at: desc
    input:
      user_id: { type: string, required: true }

Immutability

The ledger is append-only by application design:

  • causet-runtime-service contains no UPDATE or DELETE queries against ledger_events
  • The database schema has NOT NULL constraints on critical fields
  • Sequence numbers and timestamps create a detectable record if rows are manually modified or deleted

For deployments where database-level immutability is required, add PostgreSQL row-level security:

-- Prevent anyone (including superusers) from updating or deleting ledger events
-- via the application database user
REVOKE UPDATE, DELETE ON causet.ledger_events FROM causet_runtime;
REVOKE UPDATE, DELETE ON causet.ledger_events FROM causet_worker;

This does not prevent a database administrator from modifying rows, but limits the blast radius of application-layer vulnerabilities.


Retention policy

Ledger events accumulate without bound by default. Define a retention policy based on your compliance requirements:

Compliance frameworkTypical retention
GDPR (EU)As long as necessary for purpose; delete when no longer needed
SOX (financial)7 years
HIPAA (healthcare)6 years
PCI DSS1 year minimum

Implement retention as a scheduled job that archives old events to cold storage before deleting:

# 1. Export events older than 2 years to S3 Glacier
aws s3 cp \
  <(psql $DB -c "COPY (SELECT * FROM causet.ledger_events WHERE created_at < NOW() - INTERVAL '2 years') TO STDOUT CSV HEADER") \
  s3://causet-audit-archive/ledger-events-$(date +%Y%m%d).csv
 
# 2. Verify archive integrity
# 3. Delete from live table
psql $DB -c "DELETE FROM causet.ledger_events WHERE created_at < NOW() - INTERVAL '2 years'"

Warning: Deleting ledger events breaks the ability to replay entity history for the deleted period. Do this only after archiving and only for data outside your operational retention window.


Access control for audit data

Restrict direct access to the ledger_events table to:

  • The causet_runtime database user (application writes)
  • A dedicated causet_audit read-only user for compliance queries
  • Database administrators (logged access)

Do not expose raw ledger access to application users. Build projection-based audit reports with appropriate access control through the query service.