Module: database/repositories/auditDlqRepo

SEC-007 audit-log SIEM dead-letter queue.

When the SIEM forwarder (see utils/notifications.js SIEM target) fails to deliver an audit event after its retry budget is exhausted, the row's snapshot is dropped into the audit_dlq table so an operator can inspect and manually replay it later. The schema is defined by migration 031.

Schema (migration 031_activities_compliance.sql)

Column Type Notes
id TEXT PK Short opaque ID, DLQ-<n> (see below).
workspaceId TEXT Owning workspace (used for the admin scope).
rowSnapshot TEXT JSON-stringified activity row.
lastError TEXT Most recent dispatch error message.
attempts INTEGER Count of dispatch attempts (incl. retries).
createdAt TEXT ISO timestamp of the first failure.

ID convention

The audit_dlq.id column is TEXT PRIMARY KEY, so any unique opaque string works. We use DLQ-<n> (counter via counterRepo) to match the project-wide short-ID convention (TC-, RUN-, ACT-, …) — readable in admin UI lists without exposing internal UUIDs.

Source:

Methods

(static) countByWorkspace(workspaceId) → {number}

Count of DLQ rows for a workspace — used by the AuditLog UI to render the "N retry-failed" badge without loading the full list.

Parameters:
Name Type Description
workspaceId string
Source:
Returns:
Type
number

(static) enqueue(entry) → {Object}

Insert a fresh DLQ row for an activity that could not be delivered to the configured SIEM target after exhausting the retry budget.

Always starts at attempts = 1 — the initial delivery attempt counts as the first attempt. The forwarder is expected to retry in-process up to its configured budget before calling this; subsequent UI-triggered /replay invocations increment via incrementAttempts().

Parameters:
Name Type Description
entry Object
Properties
Name Type Description
workspaceId string

Owning workspace; the admin replay route gates by this so DLQ rows from workspace A can never be replayed by an admin of workspace B.

rowSnapshot Object

The full activity row that failed to dispatch. Stored as a JSON string; readers re-parse via hydrate().

lastError string

Most recent dispatch error message.

Source:
Returns:

The created DLQ row (hydrated — rowSnapshot is an object, not a JSON string).

Type
Object

(static) getById(id) → {Object|null}

Fetch a single DLQ row by ID. Returns null when missing. Callers must still cross-check row.workspaceId === req.workspaceId before acting on it — the route layer is the trust boundary for cross-workspace access.

Parameters:
Name Type Description
id string
Source:
Returns:
Type
Object | null

(static) incrementAttempts(id, lastError) → {number}

Increment the attempt counter and record the most recent error after a failed replay. Returns the new attempts count, or 0 if the row no longer exists (e.g. another admin removed it concurrently).

Parameters:
Name Type Description
id string
lastError string
Source:
Returns:
Type
number

(static) listByWorkspace(workspaceId, optsopt) → {Array.<Object>}

List DLQ rows for a workspace, newest-first. Caller (route handler) is responsible for the admin gate and workspace-scope assertion.

Parameters:
Name Type Attributes Description
workspaceId string
opts Object <optional>
Properties
Name Type Attributes Default Description
limit number <optional>
200

Hard-capped at 1000 defensively.

Source:
Returns:
Type
Array.<Object>

(static) remove(id) → {boolean}

Delete a DLQ row after a successful replay. Returns true when a row was removed, false when none matched (idempotent for the caller).

Parameters:
Name Type Description
id string
Source:
Returns:
Type
boolean