Skip to content

Audit log

Append-only record of state-changing admin actions — who did what, when, against what target.

The audit log captures every state-changing call to /api/v1/admin/* — settings updates, hook saves, admin invites, backup triggers, and so on — every collection write at /api/v1/collections, every record restore, and every MCP tool call. 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/* and to /api/v1/collections (create, update, delete a collection). 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 audit rows, and deletes them only through the opt-in audit.retention_days setting (entries older than that many days are removed by the daily cleanup; 0, the default, keeps them forever). Operators can also 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 or name from the path
status INTEGER NOT NULL,
ip TEXT, -- client IP (see IP capture); null for MCP rows
summary TEXT, -- optional human / JSON
at INTEGER NOT NULL, -- unix-seconds
token_id TEXT, -- API token of an MCP call; null otherwise
token_name TEXT -- that token's name at the time
);

Indexes on (actor_id, at), (at), (action, at), (token_id, at). token_id and token_name are filled since 0.40.0; older MCP rows name the token in summary only.

action is derived from the path + method. Examples:

MethodPathaction
POST/api/v1/collectionscollections.create
PATCH/api/v1/collections/<id>collections.update (target = the id)
PATCH/api/v1/admin/settingssettings.update
POST/api/v1/admin/hookshooks.create
POST/api/v1/admin/adminsadmins.create
POST/api/v1/admin/sql/runsql.create (target = run; the SQL text is not stored)

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

MCP tool calls are entries with action mcp.tool, method MCP and the tool name as path; run_sql writes mcp.sql.read / mcp.sql.write. See MCP audit trail. A call on a record has collection=<name> id=<id> as its target.

A record restore is record.restore, with the record as target and the restored version in summary.

GET /api/v1/admin/audit-log
?page=1&perPage=50
&actorId=<id>
&tokenId=<api-token-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
}
}

GET /api/v1/admin/audit-log/tokens lists the API tokens that appear in the log (id, name, number of calls, last call), newest first, deleted tokens included.

Sidebar → System → Audit log. Filter by actor id, API token, action prefix, and date range; click a row to see the full entry (path, status, target, token, IP, timestamp, summary). From an entry, Only this token filters to that token’s calls, and Record history opens the record it names with its history, so you can restore an earlier version or undo a delete. The table paginates server-side; default page size 50.

The audit row’s ip field is the connecting peer’s address. X-Forwarded-For is used only when the peer is a trusted proxy (VAULTBASE_TRUSTED_PROXIES or security.trusted_proxies). Without that, a client can’t put a fake address in the column by sending the header. MCP tool calls have no request to read from, so their ip is null.

/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.