Audit Logs¶
Doable keeps two independent audit trails that together cover privileged admin activity and integration call activity.
| Table | What it tracks | Who can view |
|---|---|---|
admin_audit_log |
Every action a platform admin takes on the audit surface (conversation search, session view, stats, log reads) | Platform admins only |
connector_audit |
Every integration/MCP call made through the connector proxy: status, latency, user | Platform admins; workspace-scoped data visible to workspace owners through the connector proxy API |
What gets recorded¶
admin_audit_log: Platform admin actions¶
Recorded whenever a platform admin touches the audit or operations surface:
| Action string | Trigger |
|---|---|
audit.conversations.search |
Admin searches the conversation list (/admin/audit) |
audit.conversation.view |
Admin opens a specific AI session transcript |
audit.messages.search |
Admin performs a full-text message search |
audit.actions.search |
Admin views the admin action log itself |
audit.stats.view |
Admin loads aggregate usage statistics |
view_project_logs |
Admin reads a project's runtime log output |
The act of auditing is itself audited
Every read on the audit surface writes a new admin_audit_log row. Admins cannot browse conversations without leaving a trace.
connector_audit: Integration and MCP proxy calls¶
Written on every request through the connector proxy, regardless of outcome:
| Status | Meaning |
|---|---|
ok |
Call succeeded |
denied |
Blocked by tool-scoping, rate limit, allowlist, or missing connection |
error |
Upstream integration returned an error |
Fields captured¶
admin_audit_log¶
| Field | Type | Description |
|---|---|---|
id |
bigserial | Auto-incrementing primary key |
ts |
timestamptz | Event timestamp (server clock) |
actor_id |
uuid | Platform admin who triggered the action |
actor_email |
text | Email at time of action (denormalized) |
actor_role |
text | Always platform_admin |
action |
text | Dot-separated action string (see table above) |
resource_type |
text | session, message, user, workspace, project, project_runtime |
resource_id |
text | Free-form identifier, usually a UUID |
target_user_id |
uuid | Subject user, if applicable |
target_workspace_id |
uuid | Subject workspace, if applicable |
target_project_id |
uuid | Subject project, if applicable |
details |
jsonb | Structured context: filters used, result counts, query parameters |
client_ip |
inet | Resolved from cf-connecting-ip, then x-forwarded-for, then socket |
user_agent |
text | Browser or tool making the request |
connector_audit¶
| Field | Type | Description |
|---|---|---|
id |
uuid | Row identifier |
project_id |
uuid | Project the proxy call was made on behalf of |
integration |
text | Integration slug (e.g. github, mcp) |
action |
text | Tool or action name |
user_id |
uuid | Calling user (if authenticated; null for service-key calls) |
status |
text | ok, denied, or error |
duration_ms |
int | End-to-end call latency |
ts |
timestamptz | Event timestamp |
Where to view¶
Conversation audit (/admin/audit)¶
Go to Admin, Audit (platform admins only). The page shows:
- Aggregate stats: total sessions, messages, users; 24-hour and 7-day message counts
- A searchable conversation list filtered by user ID, workspace ID, project ID, time range, and message content substring
- Click any row to read the full session transcript at
/admin/audit/<sessionId>
Admin action log (/admin/audit/actions)¶
Click Admin action log in the top-right of the audit page. Filter by actor ID, action type, and time range to see who searched or viewed what, and when.
Retention¶
Neither table has a scheduled cleanup job in the current schema. Records accumulate indefinitely. To control growth:
- Partition
admin_audit_logby month using PostgreSQL declarative partitioning and drop old partitions. - Archive rows older than your retention policy to cold storage before deletion.
connector_auditis indexed on(project_id, ts DESC); queries stay fast even at large volume, but disk growth is unbounded without pruning.
Operators are expected to handle retention according to their own compliance requirements.
Exporting¶
There is no built-in CSV or JSON export button in the current UI. To export programmatically:
-- Export admin action log for a date range
COPY (
SELECT * FROM admin_audit_log
WHERE ts >= '2025-01-01' AND ts < '2025-02-01'
ORDER BY ts DESC
) TO '/tmp/admin_audit_jan.csv' WITH CSV HEADER;
-- Export connector audit for a project
COPY (
SELECT * FROM connector_audit
WHERE project_id = '<uuid>' ORDER BY ts DESC
) TO '/tmp/connector_audit.csv' WITH CSV HEADER;
Run these as the postgres superuser or a role with pg_write_server_files.
Trust model¶
- Append-only by design. The migration comment in
059_admin_audit.sqlis explicit: "No UPDATE/DELETE policy; operators rotate by partitioning or archiving the table, never by mutating rows." There is no API endpoint that deletes or modifies audit rows. - Admin read access, no delete. Platform admins can query and filter logs through the UI. No route exposes
DELETEorUPDATEon either audit table. - Audit failures are non-blocking.
recordAdminActionand the connectoraudit()function are fire-and-forget; a database hiccup will not fail the admin request, but the missed row is logged to stdout ([admin-audit] insert failed).
What is NOT recorded¶
- Request or response bodies of end-user AI messages (content is stored in
ai_messages, not duplicated into the audit log). - Full SQL queries: only structured metadata (action, resource IDs, filters) is captured.
- Ephemeral session state such as WebSocket connection metadata or in-memory rate-limit counters.
- Actions taken by non-admin users (workspace member operations, project file edits, chat turns). Those are covered by the application data model, not the audit log.
Workspace owners¶
The admin_audit_log surface is platform-admin only; workspace owners have no UI access to it and the API routes are guarded by isPlatformAdmin checks.
connector_audit rows are project-scoped. Workspace owners can query this table for their own projects via direct database access if they self-host, but there is no dedicated workspace-owner UI for it in the current release.
See also¶
- Logs & Monitoring: stdout log streams, journalctl, Docker log drivers
- Security Model: trust boundaries and the five security layers
- Secrets: encryption of stored credentials