The read API ships as audit.query on the app returned by startTracevault, and as audit.getScope("name").query per scope. It is deliberately narrow: equality filters on scalar columns (including generated outcome, error_code, severity), an occurred_at window, and deterministic pagination. Aggregations, JSONB path probes, and joins belong in raw SQL.
const recent = await audit.query.findMany({
event: "product.price.updated",
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
limit: 100,
})
const scoped = await audit.getScope("users").query.findMany({
event: "user.profile.updated",
limit: 50,
})
const one = await audit.query.findById("uuid-here")
const total = await audit.query.count({ actorType: "user", environment: "prod" })Use getScope("users").query instead of a separate reader factory. All scopes share the app's read and write pools.
const rows = await audit.getScope("users").query.findMany({
event: "user.profile.updated",
errorsOnly: true,
limit: 50,
})errorsOnly: true matches rows where outcome = 'failure' or severity is in SEVERITIES_FOR_ERRORS_ONLY_FILTER (exported from tracevault). Filters are ANDed together.
// failures by outcome OR high-severity rows (see SEVERITIES_FOR_ERRORS_ONLY_FILTER)
await audit.query.findMany({
errorsOnly: true,
limit: 50,
})Unknown keys throw ValidationError. String filters are plain equality — no LIKE. from / to must not be inverted.
| Field | Applies to | Notes |
|---|---|---|
| event | findMany, count | Exact match |
| actorId, actorType | findMany, count | Exact match |
| targetId, targetType | findMany, count | Exact match |
| correlationId, requestId | findMany, count | Exact match |
| environment | findMany, count | Exact match |
| outcome, errorCode, severity | findMany, count | Generated columns (migrations 002–003) |
| severities | findMany, count | IN list, max 16, no duplicates |
| errorsOnly | findMany, count | failure outcome or high severities |
| mode | findMany, count | sync | async |
| from, to | findMany, count | Inclusive bounds on occurredAt |
| limit, offset, order | findMany only | limit 1–500 (default 50), order asc|desc on (occurred_at, id) |
Call await audit.close() once: it drains every scope's write queue, then ends Tracevault-owned pools. Injected pool / readPool are not ended. After close, emit and query throw TracevaultError.
AuditRecord, AuditQueryFilters, DOCUMENTED_SEVERITY_LEVELS, and related exports live on the main tracevault entry — no separate query package in 1.x.
Column reference on Data model. setup on Install.