# Audit V2 Pipeline — Operator Runbook

C10.5 wires the C4–C10 audit machinery (DBEmitter, OutboxWorker, field-level
expansion, FLS redaction, Entrypoint ctx propagation) into the live request
path. This document describes the runtime knobs and the backward-compat path.

## Environment variables

### `AUDIT_V2_ENABLED` (default `true`)

Controls whether `PersistenceService` calls the V2 DBEmitter on UPDATE.

- **`true` (default)**: UPDATE path writes BOTH the legacy field rows (via
  `generateAuditLogs`) AND the V2 head row + outbox payload (via
  `DBEmitter.EmitTx`). Head rows are visible to the C11 `<Object>History`
  read API; outbox rows wait for the worker to drain them into field rows.
- **`false`**: UPDATE path behaves exactly as before C10.5 — only the legacy
  writer runs. Use this to roll back V2 without a deploy.

Scope: UPDATE only for C10.5. CREATE and DELETE emission lands in a
follow-up commit.

### `AUDIT_V2_WORKER_ENABLED` (default `false`)

Controls whether the `OutboxWorker` goroutine starts at backend boot.

- **`false` (default)**: worker is NOT started. Head rows still land
  synchronously in the business tx, so `<Object>History` remains
  functional. Outbox rows accumulate in `_System_AuditOutbox` and will be
  drained when the worker comes online.
- **`true`**: backend main calls `audit.ResolveOutboxStore(db.DB())`. If
  the resolver returns a non-nil `OutboxStore`, the worker is started as a
  goroutine with a ctx bound to server shutdown. If the resolver returns
  nil (current state — MySQL-backed store not yet wired), a WARN is logged
  and the worker is skipped.

When the MySQL-backed `OutboxStore` lands, set this env var to `true` in
the target environment and restart the process.

## Startup / shutdown

- Worker start: after `svcMgr.StartBackgroundWorkers()`, before the HTTP
  listener begins serving.
- Worker stop: on SIGINT / SIGTERM, `auditWorkerCancel()` signals the
  worker ctx; `OutboxWorker.Run` exits on the next poll tick (default
  250 ms).
- Logs:
  - Enabled + store resolved: `📝 Audit V2 outbox worker started`
  - Enabled + no store: `⚠️  AUDIT_V2_WORKER_ENABLED=true but no OutboxStore implementation available yet — worker NOT started`
  - Disabled: `📝 Audit V2 outbox worker disabled (set AUDIT_V2_WORKER_ENABLED=true to enable)`
  - On shutdown: `🛑 Audit V2 outbox worker signalled to stop`

## Entrypoint attribution

`AuditEntrypointMiddleware` runs immediately after `RequestID` middleware
and stamps one of:

- `EntrypointBulkAPI` when the request carries `X-NexusCRM-Bulk: true`
  (also accepts `1`, `yes`, case-insensitive)
- `EntrypointRestAPI` otherwise

Flow / trigger execution paths override the REST entrypoint via
`audit.WithEntrypoint` inside the executor boundary (C9 scope) — those
emitters see `EntrypointFlow` / `EntrypointTrigger` instead of `REST_API`.

## Backward compatibility

`generateAuditLogs` (the pre-C10.5 writer) is unchanged and still runs
on every UPDATE. Setting `AUDIT_V2_ENABLED=false` simply skips the new
code path. No migration is required; the new `_System_AuditOutbox` table
and the new columns on `_System_AuditLog` were added in prior commits and
are backward-compatible (NULL defaults).
