What is recorded, and what is not
Mutations only.POST, PUT and DELETE are recorded; a GET never is. Reads are the overwhelming majority of traffic and recording them would bury the thing you came to find.
Four categories are deliberately excluded, and knowing them is the difference between “there is no record” and “I looked in the wrong place”:
What a row holds
Bodies are captured, and secrets are redacted
Both the request and the response body are stored, which is what makes the log usable for debugging rather than only for accountability — you can see the payload that produced a400.
Values are redacted by key name, case-insensitively, wherever the key contains password, secret, token, apikey, api_key, credential, privatekey, kubeconfigyaml or sshpassword. The value becomes "***". The redaction runs over raw bytes rather than parsed JSON, so it still works on a malformed body — worst case it leaves the input untouched rather than failing open on a half-parsed object.
Redaction keys on the name. A secret sent under a neutral key — a certificate body in
content, a credential in data — is not recognised as one and is stored as sent. That is a reason to keep secret material in conventionally-named fields, which is also what the rest of the platform does.Updates carry a diff
Before aPUT reaches its handler, the current state of the resource is snapshotted; afterwards the two are compared and the field-level difference is stored. The detail view renders it as from → to per field, so an update reads as what actually changed rather than as two JSON blobs to compare by eye.
Failures are first-class
The status code is stored and filterable. A4xx is usually a permission denial or a rejected payload; a 5xx carries the panic’s stack trace when there was one. Filtering to failures is the fastest way to see what an operator tried to do and could not.
Finding things
The four tiles across the top count, for the window and filters in view:
With no window chosen, the page covers all time, and the time picker says so.
Listing is paged — 50 rows by default, 200 at most per request.
The page does not refresh by itself unless you ask it to. Auto-refresh every under the list re-reads it every 15, 30 or 60 seconds. Live tail beside Export re-reads it every 15 seconds while it is on.
Export
Export honours the filters currently applied and returns up to 10 000 rows, ascsv (the default) or json. It is a download, not a report: use it to hand a window to someone who does not have access to the platform, or to keep a copy of an incident before the window scrolls past.
- UI
- API
- MCP
/audit-logs — tiles, filter sidebar, timeline, Live tail and auto-refresh, and a detail drawer per row with the diff and both bodies.
Permissions
There is one action, and it covers reading the log in every form. Nothing writes to it through the API — rows are produced by the middleware as a side effect of other work, and there is no route that edits or deletes one.
Retention
Celum never deletes audit rows. There is no purge job and no retention setting: the table grows for as long as the database keeps it, which is the right default for a record whose value is that it was not tidied up. It is also typically the largest table in the control-plane database, so it is the one to watch when sizing storage or taking a database backup.What commonly goes wrong
Related
Settings
API tokens, database backup, and the rest of the platform-wide configuration.
Permissions model
How a denial is decided — the
4xx rows in this log are its output.Permissions reference
Every action, including
audit-log:List.Celum AI and MCP
Why an assistant’s work appears under its real actions rather than as MCP calls.