Audit Events
In Causet, the event ledger is your audit log. Every state change in the system results in a committed event in ledger_events. This record is append-only, covers all state changes by construction, and does not require you to build or maintain a separate audit system.
What the ledger records
Every event appended to the ledger contains:
| Field | Description |
|---|---|
event_id | Unique identifier for this event |
event_type | The event name (e.g., ARTIST_FOLLOWED) |
entity_id | The entity this event applies to |
entity_type | The entity type (e.g., user) |
payload | The full event payload (as JSONB) |
fork_id | The fork (environment) this event belongs to |
platform_id | The platform |
application_id | The application |
correlation_id | Links this event to the intent that caused it |
created_at | Timestamp (microseconds) when the event was committed |
Why the ledger is a better audit trail
Compared to a manually maintained audit table:
It cannot be accidentally omitted. The runtime emits events as part of rule evaluation — there is no code path that changes state without producing an event. A manual audit table depends on developers remembering to write audit rows in every code path.
It cannot be retroactively modified. Events are appended, never updated. The ledger_events table has no UPDATE or DELETE paths in the application. A separate audit table can be corrupted or cleared by a developer with DB access.
It covers all state changes. Every action that changes entity state goes through the runtime and produces events. Partial state changes are impossible — the rules engine is atomic.
It is queryable. The ledger is a PostgreSQL table. You can query it with standard SQL for specific entities, time ranges, event types, or correlation IDs.
Querying audit history
-- All events for a specific user
SELECT event_type, entity_id, payload, created_at
FROM causet.ledger_events
WHERE entity_id = 'user-1'
AND fork_id = 'production'
ORDER BY created_at ASC;
-- All ARTIST_FOLLOWED events in the last 24 hours
SELECT event_id, entity_id, payload, created_at
FROM causet.ledger_events
WHERE event_type = 'ARTIST_FOLLOWED'
AND fork_id = 'production'
AND created_at >= NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC;
-- Trace what caused a specific state change (by correlation ID)
SELECT event_type, entity_id, payload, created_at
FROM causet.ledger_events
WHERE correlation_id = 'corr-abc123';Audit projections
For specific compliance query shapes, create a projection table that materializes the audit data you need:
# projections/audit.projections.causet
projections:
artist_follow_audit:
source_events: [ARTIST_FOLLOWED, ARTIST_UNFOLLOWED]
target:
table: artist_follow_audit
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 materialized view is optimized for audit queries (indexed, specific shape) while the underlying ledger remains the authoritative append-only record.
Replay for forensics
To understand the state of an entity at any point in time, replay events up to a specific timestamp:
-- All events for user-1 before a specific timestamp
SELECT event_type, payload
FROM causet.ledger_events
WHERE entity_id = 'user-1'
AND created_at < '2026-06-25 18:00:00'
ORDER BY created_at ASC;Apply these events in order to reconstruct entity state at that point. This is the same mechanism the runtime uses internally — entity state is always derived from events, never stored independently.
Immutability guarantees
The ledger is append-only by application design, not by database constraint. In practice:
- The
causet-runtime-servicehas noUPDATEorDELETEquery paths forledger_events - Database-level constraints (
NOT NULL, foreign keys) protect data integrity - Row-level security or PostgreSQL publication can be configured to prevent manual tampering if required for compliance
Note: Causet does not provide cryptographic signing of ledger events. The ledger is tamper-evident in the sense that modifying historical events would require direct database access and would leave detectable gaps in sequence numbers and timestamps. For cryptographic immutability guarantees (e.g., for regulated industries), integrate a separate ledger technology like a blockchain or immutable audit log service.
PII in audit logs
The ledger is immutable — events cannot be deleted. This creates tension with GDPR right-to-erasure obligations.
If event payloads contain PII, deletion is not possible without corrupting the audit trail. Design your events to contain opaque IDs rather than PII values.
See PII Handling for specific strategies (cryptographic erasure, pseudonymization).
Retention
Ledger events accumulate indefinitely by default. For long-running applications, the ledger_events table grows continuously. Implement a retention policy based on your compliance requirements:
-- Archive events older than 2 years to cold storage, then delete
-- (implement this as a scheduled job, not a manual operation)
DELETE FROM causet.ledger_events
WHERE created_at < NOW() - INTERVAL '2 years'
AND fork_id = 'production';Warning: Deleting events breaks the ability to replay entity history for those events. Only delete events after archiving them to cold storage (e.g., S3 Glacier) and verifying the archive is complete.