Phase 6: append-only audit trail for LLM calls and tool invocations

Archibald312 added a tamper-resistant audit log covering every LLM call and tool invocation - database triggers block UPDATE and DELETE outright, so an app-layer compromise can append rows but not rewrite history. The groundwork is explicitly tied to paid data connectors coming in a later phase: before third-party data flows in, the system needs a provable record of who saw what.

complianceinfrastructure

The audit_log table lives in Postgres and carries two triggers (audit_log_no_update, audit_log_no_delete) that raise an exception on any modification attempt. The anon and authenticated roles are revoked from the table entirely - only the service role writes to it.

Instrumentation wraps two chokepoints. The tool dispatcher records one row per invocation: duration in milliseconds, SHA-256 hashes of input and output payloads (not the raw content), resolved document IDs, and status (success, error, or blocked). The LLM streaming adapter records one row per outer call. Archibald312 chose per-outer-call granularity deliberately rather than per-provider iteration inside tool-use loops, keeping the table compact and aligned with what a user would recognize as a single operation.

user_email is denormalized alongside the user_id FK. If the user row is later deleted, the FK nulls out but the email stays visible to investigators. Worth noting if your fork operates under data minimization rules.

The write path is fire-and-forget: audit errors are swallowed so they never break the request path. A GET /audit-log endpoint exposes a user's own rows filtered by project, event type, and time range - self-service, not admin-scoped. The schema reserves connector_fetch, document_upload, and document_download event types that nothing emits yet; those wire up when the connector work lands.

No retention policy ships here. The table grows without bound; operators need their own archival process.

So what Worth a look if you have compliance requirements, B2B procurement checklists, or are heading toward paid data connectors where you need to prove data lineage. The DB-trigger immutability and the hash-not-content approach are patterns worth copying regardless of fork direction. Skip if you have no regulated-industry or audit angle - the table will just accumulate rows with no payoff.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?