DBTrail
DBTrail EE

Audit trail

What the audit feature records, the JSONL format, the fail-closed startup policy, and how write failures surface

One JSON line per auditable operation, in a local file your log shipper already watches. Offline by design. Needs the audit license feature; without it nothing is written: Licensing.

At startup, with a license that claims audit. If the license is enabled (valid or grace) and the audit file opens, DBTrail runs and logs every access, append-only with mode 0600. If the file does not open, it exits 1 and refuses to run unaudited, unless DBTRAIL_AUDIT_OPTIONAL=1. If the license is not enabled, it runs unaudited and prints audit trail DISABLED. A write failure mid-run never stops the operation: the record is lost and a warning prints at most every 30 seconds.

What it records

DBTrail never executes SQL against your databases, so an event is either historical data access (query, time-travel read, reconstruct) or recovery script generation.

  • CLI: query, recover, recover-cascade, reconstruct, verify --explain
  • MCP tools, the time-travel shim, and the web console, including its RBAC denials
  • Out of scope: metadata-only reads and the capture plane

Where it writes

$DBTRAIL_AUDIT_LOG, else /var/log/dbtrail/audit.jsonl. Parent directories are created; the file is append-only, mode 0600, one unbuffered write per record.

export DBTRAIL_AUDIT_LOG=/var/log/myco/dbtrail-audit.jsonl

The JSONL format

Field names are capitalized: Time (UTC, RFC 3339), Surface (cli, mcp, shim, console), Actor (os:<user>, plus profile:<name> when a profile is active; network surfaces record the authenticated user), Action, Schema, Table, Detail.

{"Time":"2026-07-16T14:03:22.481Z","Surface":"cli","Action":"query.run","Actor":"os:alice","Schema":"shop","Table":"orders","Detail":{"results":"12","format":"table"}}
{"Time":"2026-07-16T14:05:01.902Z","Surface":"cli","Action":"recover.generate","Actor":"os:alice profile:auditor","Schema":"shop","Table":"orders","Detail":{"statements":"3","dry_run":"false","output":"undo.sql","gtid":""}}
jq -r 'select(.Action=="recover.generate") | "\(.Time) \(.Actor) \(.Schema).\(.Table)"' \
    /var/log/dbtrail/audit.jsonl

Fail-closed policy

The gate

License enabled (valid or grace) and claiming audit and the sink cannot open at startup: dbtrail-ee exits 1 instead of running unaudited. An expired or disabled audit license never trips it.

  • Escape hatch: DBTRAIL_AUDIT_OPTIONAL=1 turns the exit into a warning and runs without the trail.
  • Still allowed with a dead sink, so you can diagnose it: bare dbtrail-ee, -h, --help, -v, --version, and the license, help and completion subcommands. Everything else fails closed.
error: audit trail could not be initialized: <reason>
this license includes the "audit" feature; refusing to run unaudited (fail closed).
Fix the audit log destination (DBTRAIL_AUDIT_LOG, default /var/log/dbtrail/audit.jsonl) or set DBTRAIL_AUDIT_OPTIONAL=1 to run without the audit trail.

Write failures at runtime

A write that fails after startup (disk full, directory rotated away) never blocks or crashes the operation: a recovery must finish. The record is lost for good, and an audit: write failed warning prints at most once every 30 seconds with lost_since_last_warning. Treat it as an incident.

When an audit license lapses

A license claiming audit that is not enabled (expired, updates-lapsed, invalid, too-new schema) prints an audit trail DISABLED line on every start, on top of the license banner. Its remedy follows the cause: upgrade dbtrail-ee, run a release inside the updates window, or renew. Expiry, grace, and the updates window.

On this page