Skip to content

Audit ​

Read-only listing over the workspace's append-only audit log. Every mutating admin route (POST / PATCH / DELETE) writes one AuditEntry row capturing the actor principal, action verb, target entity, and a JSON-safe before/after snapshot. This endpoint is the authoritative "what happened on my workspace" surface — dashboards and SOC exports read it, and incident response pivots from it into /admin/inference via inference_log_id.

The router lives at packages/shared/audit_routes.py. The persistence layer is AuditLogRepository (packages/shared/audit.py:58).

Endpoints ​

MethodPathAuthSummary
GET/admin/auditBearerWorkspace-scoped audit listing, ordered by ts DESC.

GET /admin/audit ​

Query parameters (all optional, all AND-composed):

ParamTypeNotes
actionstringExact match (e.g. adapter.activate).
entitystringExact match (e.g. adapter:adp_…).
actorstringExact-match on the actor_principal column.
sinceISO-8601 datetimeInclusive UTC lower bound.
untilISO-8601 datetimeInclusive UTC upper bound.
limitint (1–500)Default 100.
offsetint (≥0)Default 0.

Workspace is always taken from the bearer principal — there is no query string for it.

Response shape:

json
{
  "entries": [
    {
      "id": "aud_01HF…",
      "workspace_id": "default",
      "actor_principal": "operator",
      "action": "adapter.activate",
      "entity": "adapter:adp_01HF…",
      "before": null,
      "after": {
        "model_id": "llama3.2:3b",
        "adapter_id": "adp_01HF…",
        "swap_ms": 412.3
      },
      "ts": "2026-05-01T12:00:00Z",
      "inference_log_id": null
    }
  ],
  "total": 1,
  "limit": 100,
  "offset": 0
}

Rows are ordered by ts DESC so the most recent action comes first. total reflects only the page payload size — there is no full-table count for v0; pagination uses limit/offset.

bash
curl -H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
  "http://localhost:8000/admin/audit?action=adapter.activate&since=2026-04-01T00:00:00Z"

Operator notes ​

  • Workspace fence. Every row is filtered by the principal's workspace_id; cross-tenant existence does not leak. See workspace fence.
  • Append-only. There is no DELETE route by design. Retention is the operator's responsibility — drop rows from the audit_logs table directly when policy demands it.
  • Cross-layer correlation. When the audited action followed a logged inference, inference_log_id links back to /admin/inference/{id} (added in migration 0016_cross_layer_correlation).
  • 503 causes. app.state.audit_repo is wired in lifespan; the route returns 503 with "AuditLogRepository not initialised" when the lifespan did not complete.
  • Best-effort writes. Mutating admin routes write audit rows in fire-and-forget fashion; a transient audit-store outage never blocks the originating call. The corollary: a missing audit row is not proof a mutation did not happen — also check /admin/inference and the relevant resource state.

Dual-licensed: MIT OR Apache-2.0