Audit log
Not in
openapi.jsonyet. Written fromsrc/ResilientLang.Eventsand itssrc/ResilientLang.Events/docs/WEBHOOKS.md; a test checks that the routes exist in the source.
Every change that matters (webhook created, site published, role changed, key rotated, and so on) is written to a per-tenant log. Each entry is chained to the previous one with an HMAC, so a modified or missing entry is detectable.
Routes#
| Method and path | Scope | Purpose |
|---|---|---|
GET /v1/audit | audit:read | Entries, newest first. Filters: action, actor, siteId, from, to; paging with cursor and limit |
GET /v1/audit/export?format=csv|json | audit:read | Export, at most 10,000 rows. CSV cells that start with =, +, - or @ are prefixed with ' so spreadsheets do not run them |
GET /v1/audit/verify | audit:read | Recomputes the chain and reports the first modified or missing entry |
GET /v1/audit/settings | audit:read | Current retention |
PUT /v1/audit/settings | audit:manage | { "retentionDays": 30..730 } (default 365; the bounds are proposals awaiting the owner) |
Entry#
Sequence number, time, actor (user, api_key, system or operator, plus an id), action (for example webhook.created, site.published), target, outcome, site, client IP (the last IPv4 octet is zeroed; IPv6 beyond /48 is zeroed), a small redacted metadata map, and the chain HMAC.
What verification does and does not prove#
verify detects edits and deletions inside the chain. Retention deletes only the oldest entries and keeps a checkpoint so the rest still verifies. Detecting that the newest entries were cut off needs the periodic signed chain head that the platform hands to an external store; whether that store is configured is a hosting decision (verify). The log does not replace access control on the database.