Skip to content

Audit log

The audit log captures every state-changing call to /api/v1/admin/* — collection edits, settings updates, hook saves, admin invites, file uploads, backup triggers, and so on. One row per request, append-only, designed for compliance and forensic lookups.

A row is written for every POST / PUT / PATCH / DELETE to /api/v1/admin/*. GET is not audited — read traffic is high-volume and already covered by the request log.

Skip-list (intentionally not audited):

  • /api/v1/admin/auth/login, /api/v1/admin/auth/logout — auth attempts already log via the request log
  • /api/v1/admin/setup — fresh-install bootstrap
  • /api/v1/admin/migrations/diff — read-only despite being POST
  • Any path matching /api/v1/admin/preview-* or /api/v1/admin/.*/preview$

Each row is immutable. The application never updates or deletes audit rows; operators who need to prune for storage budget can DELETE directly from the vaultbase_audit_log table.

CREATE TABLE vaultbase_audit_log (
id TEXT PRIMARY KEY,
actor_id TEXT, -- admin id from the JWT
actor_email TEXT, -- email cached at the time of action
method TEXT NOT NULL,
path TEXT NOT NULL,
action TEXT NOT NULL, -- logical label, e.g. "collections.create"
target TEXT, -- best-effort id from the path
status INTEGER NOT NULL,
ip TEXT, -- only set when VAULTBASE_TRUSTED_PROXIES is configured
summary TEXT, -- optional human / JSON
at INTEGER NOT NULL -- unix-seconds
);

Indexes on (actor_id), (at DESC), (action).

action is derived from the path + method. Examples:

MethodPathaction
POST/api/v1/admin/collectionscollections.create
PATCH/api/v1/admin/collections/postscollections.update
DELETE/api/v1/admin/admins/admin-42admins.delete (target = admin-42)
POST/api/v1/admin/hooks/123/testhooks.test

Uncategorised paths fall back to <METHOD> <path>.

GET /api/v1/admin/audit-log
?page=1&perPage=50
&actorId=<id>
&actionPrefix=collections.
&from=<unix-seconds>&to=<unix-seconds>

Returns:

{
"data": {
"data": [ { "id": "...", "actor_email": "ops@acme", "action": "collections.delete", ... } ],
"page": 1,
"perPage": 50,
"totalItems": 1234,
"totalPages": 25
}
}

Sidebar → System → Audit log. Filter by actor id, action prefix, and date range; click a row to see the full entry (path, status, target, IP, timestamp, summary). The table paginates server-side; default page size 50.

The audit row’s ip field is populated from X-Forwarded-For only when VAULTBASE_TRUSTED_PROXIES is set. Without it the column stays null — a defensive default that prevents spoofing on direct-exposed deployments.

/etc/systemd/system/vaultbase.service
Environment=VAULTBASE_TRUSTED_PROXIES=10.0.0.0/8,127.0.0.1/32

Same env var the rate-limiter uses for its real-IP resolution.