Root cause: core::audit's Postgres arm was a STUB — UDB_AUDIT_SINK=postgres
never wrote to Postgres, it warned "transport not yet wired" and fell back to
stdout, while the fail-closed startup gate accepted UDB_AUDIT_SINK=postgres as a
durable sink. Enabling fail-closed therefore left the audit trail on stdout — a
capability lie I introduced in 0.4.32 and carried through 0.4.33.
- core::audit: the Postgres sink now persists. emit_audit hands each committed
mutation to a lazily-started, bounded (8192) background writer that self-creates
the configured UDB_AUDIT_PG_TABLE (CREATE SCHEMA / CREATE TABLE IF NOT EXISTS,
mirroring the auth-plane PostgresAuditLogSink) and INSERTs off the request path.
Best-effort: a full queue / unreachable audit DB falls back to stdout with a
warning (never silently dropped) and never blocks the write. The table name is
validated as a safe qualified identifier before any interpolation (SQL-injection
guard on UDB_AUDIT_PG_TABLE). Kafka stays an honestly-warned stdout fallback.
- 4 data-plane emit sites (upsert/update/delete + tx batch) now pass the pool.
- security::durable_audit_sink_declared() is now honest: drops
UDB_AUDIT_SINK=database/durable (they resolve to AuditSinkKind::None = NO sink),
accepts only postgres/pg (now real) + the auth export UDB_AUDIT_EXPORT_POSTGRES=1.
- docs/enterprise-deployment.md §2.1: the two independent audit sinks
(UDB_AUDIT_SINK data-plane vs UDB_AUDIT_EXPORT_POSTGRES auth-plane) documented
accurately, with UDB_AUDIT_PG_TABLE required for sink=postgres.