Skip to content

Audit Logging

AuthHero captures audit events for every management API mutation — user creation, role assignment, client updates, and more. Events include the actor, the affected entity (with before/after state), the request context, and the response.

Two Modes

Default Mode (outbox disabled)

Audit events are written as LogInsert records to the logs table via waitUntil. This is fire-and-forget — if the application crashes between the entity write and the log write, the audit record is lost.

Transactional Outbox Mode

When enabled, a rich AuditEvent is written to an outbox_events table alongside the entity mutation. A background relay then transforms and delivers events to destinations (logs table, and potentially Analytics Engine, R2, webhooks).

With transactions enabled (the default, useTransactions: true in the Kysely adapter), the outbox write and entity mutation share the same database transaction. This guarantees that if the entity write succeeds, the audit event is captured. If either fails, both are rolled back.

With useTransactions: false (passthrough mode), the outbox write is best-effort and not rollback-safe. The entity mutation and outbox insert are independent writes — if the entity write fails after the outbox write succeeds, the background relay may still deliver the audit event for a mutation that was never persisted.

Configuration

typescript
import { init } from "authhero";

const { app } = init({
  dataAdapter,
  outbox: {
    enabled: true,
    captureEntityState: true,  // capture before/after entity state (default: true)
    retentionDays: 7,          // days to keep processed events (default: 7)
    maxRetries: 5,             // max delivery retries per event (default: 5)
  },
});

When outbox.enabled is false (or omitted), behavior is identical to the default mode.

The AuditEvent Type

Every audit event captures:

FieldDescription
event_typeWhat happened — e.g. user.updated, role.created
log_typeAuth0-compatible log type code (e.g. sapi)
categoryuser_action, admin_action, system, or api
actorWho performed the action — type, ID, email, scopes, client_id
targetWhat was affected — entity type, ID, before/after state, diff
requestHTTP method, path, query, body, IP, user agent
responseStatus code and response body
timestampISO 8601 timestamp

Before/After State

When captureEntityState is enabled, update operations capture the entity state before and after the mutation, plus a computed diff of changed fields. Sensitive fields (passwords, secrets) are automatically redacted.

How Events Flow

text
Request Handler

  ├─ Entity write (e.g., UPDATE user)     ┐  Same DB transaction when
  └─ Outbox write (INSERT outbox_events)  ┘  useTransactions: true (default)

                    ▼  waitUntil (after response)
              Outbox Relay

                    ├─ LogsDestination: AuditEvent → LogInsert → logs table
                    ├─ (future) R2Destination: full event as NDJSON
                    └─ (future) WebhookDestination: filtered payload

Querying Logs

The management API GET /api/v2/logs endpoints work identically in both modes. When the outbox is enabled, the relay populates the same logs table via the LogsDestination transformer.

Released under the MIT License.