Every event lands in one JSONB-first row. There are no separate old_values / new_values columns — the library stays event-oriented. See the shipped SQL under node_modules/tracevault/sql/ or use generateInitSql for an equivalent combined DDL.
| Column | Type | Notes |
|---|---|---|
| id | UUID | Generated per event. |
| event | VARCHAR | Event name (required on emit). |
| actor_id | VARCHAR | Nullable. |
| actor_type | VARCHAR | Nullable. |
| target_id | VARCHAR | Nullable. |
| target_type | VARCHAR | Nullable. |
| data | JSONB | Free-form payload. |
| meta | JSONB | Free-form metadata. |
| mode | VARCHAR | sync | async. |
| occurred_at | TIMESTAMPTZ | Provided or generated at emit-time. |
| created_at | TIMESTAMPTZ | DB default now(). |
| correlation_id | VARCHAR | Nullable. |
| request_id | VARCHAR | Nullable. |
| environment | VARCHAR | Nullable. |
| outcome | VARCHAR(64) | Generated (002+). NULLIF(BTRIM(data->>'outcome'),''). Omitted on insert. |
| error_code | VARCHAR(255) | Generated. NULLIF(BTRIM(data->'error'->>'code'),''). Omitted on insert. |
| severity | VARCHAR(32) | Generated (003+). NULLIF(BTRIM(data->>'severity'),''). Omitted on insert. |
Generated columns exist to make narrow, indexed reads pleasant via audit.query. For analytics, JSONB path filters, or joins, use the SQL tool you already rely on — Tracevault does not block raw access.
Canonical reference: README.