# Limits and operations

## Explicit events are incomplete by design

AuditLog records only events that the application writes. It cannot recover a
missing event from a changed row. SQL consoles, migrations, ETL jobs,
replication workers, and forgotten code paths can change data without producing
an event.

This is a semantic audit log, not exhaustive database provenance.

## Atomicity stops at the database boundary

A domain write and event are atomic when the caller places both in one
transaction on the same database. Writes to another database or external
service cannot join that transaction.

AuditLog does not publish events. If an application must deliver messages,
write a separate outbox row in the same transaction and let an outbox worker
handle delivery and retries.

## Time and order have narrow meanings

PostgreSQL sets `occurred_at` with `transaction_timestamp()`. It is the
transaction start time, not insertion time, commit time, or the exact time a
person acted.

All events written in one transaction share that timestamp. Queries use the
event UUID as a deterministic display tie-breaker, but UUID order does not show
insertion or causal order. Use `causation_id` to record a direct cause.

A correlation ID groups work into one request or business operation. It does
not prove that one event caused another.

## Snapshots can become sensitive or stale

Actor labels and reasons preserve what an operator supplied at the time. They
can become stale after a rename. They can also contain personal or sensitive
data and may need their own access and retention policy.

JSON metadata weakens schema guarantees. Keep it small, bounded, and limited to
queryable facts. Do not store complete row images, credentials, access tokens,
or large payloads.

## Every event maintains four indexes

The table maintains subject, actor, correlation, and global timeline indexes.
Each index serves a documented query path, but all four amplify event writes
and consume storage. Measure the cost at the application's actual event rate.

Do not add broad JSON, action, reason, or causation indexes without a measured
query that needs them.

## Append-only means ordinary DML only

An `ENABLE ALWAYS` trigger rejects `UPDATE` and `DELETE`, including DML from a
logical-replication apply worker running with
`session_replication_role = replica`.

This is not tamper evidence. A table owner or superuser can disable or replace
the trigger, use `TRUNCATE`, alter or drop the table, or edit physical backups.
Database administration remains more powerful than the trigger.

The trigger also blocks ordinary cleanup. Retention must use conspicuous,
reviewed maintenance DDL. AuditLog exposes no runtime deletion function and
no hidden bypass.

Calling `AuditLog.Migration.down/1` drops the event table and permanently
removes its contents. Use it for migration rollback only when that data loss is
acceptable.

## Retention, restore, and maintenance need runbooks

Before production use, define:

- who may read actor labels, reasons, and metadata;
- how long each class of event must remain;
- how backups and restores preserve the table and `ALWAYS` trigger state;
- how destructive retention work is reviewed, tested, and recorded;
- how to verify the trigger after replication or restore; and
- how application traffic is stopped during destructive maintenance.

Removing events changes the audit record permanently. Test retention and restore
procedures against a production-shaped copy before relying on them.
