Audit
The audit trail is the record of what happened in a tenant: every event any
module publishes becomes one row saying what happened, who caused it and when,
kept for as long as the deployment is obliged to keep it and read by whoever
holds audit:read. The module promises that a module is audited by having
emitted an event, wherever it sits in the composition; that a row is never
updated and nothing but the retention job removes one; and that recording
publishes nothing, because an audit of audits is a loop. The module is
modules/audit; this page states nothing that
tree does not.
The map
features/audit in the recordWhat it promises
Entity |
|
Events |
none: the manifest declares |
Permissions |
|
Service |
|
Routes and screens
| Method | Path | Does | Authorization | Publishes |
|---|---|---|---|---|
|
|
List the audit trail |
|
— |
|
|
Read one audit event |
|
— |
-
GET /api/v1/audit/events— the trail, newest first, filterable byname,actor,since(inclusive) anduntil(exclusive), 50 rows a page and 200 at most;GET /api/v1/audit/events/{id}— one row. Both are written by hand withhttpx.Registerininternal/handler.go, behindhttpx.Permission("audit:read"), asaudit-event-listandaudit-event-read. There is norest.Specand no command: a Spec is five routes and three of them write. -
Screens: none. The routes register no
httpx.Resource, so the shell generates nothing and the resource catalog does not list the trail. The manifest’s navigation entry "Audit" points at/admin/audit/events, a path no route serves: the shell reports it once at boot and never renders it (modules/admin/internal/mount.go,ui/page/navigation.go). Hand-written pages: none.
Authorization
| Authorization | Who passes | Routes |
|---|---|---|
|
a member of the tenant |
|
Events, jobs and subscriptions
-
Publishes: nothing.
-
Jobs:
audit-retention(internal.Retentionininternal/retention.go), hourly on0 * * * *, throughjobs.PerTenant: it deletes each tenant’s rows older thanRetentionDays, a thousand a transaction until fewer than a thousand remain, with the cutoff computed by the database so two workers' clocks cannot disagree. -
Subscriptions: one, with no name. The manifest in
module.gosetsSubscribeAll, and the kernel’smodule.Expand(kit/module/module.go) turns that one subscription into one per event every module declares, after every manifest has been read; each goes toService.Record, which insertsON CONFLICT (tenant_id, event_id) DO NOTHING— the keymigrations/000015_audit_event_unique.up.sqlgave it — so a redelivered or replayed event leaves one row. Main composes it next to last inapps/platformkit/modules.go, and nothing depends on that: a module listed after it is audited all the same.
What it needs
|
Interface |
Supplied by |
|
|
the tenant module’s |
|
|
|
Configuration: audit.retention_days in config.yaml (config.example.yaml; config.Audit in kit/config/config.go): zero means the default of 365, and a value below one is refused at load.
Who uses it
-
No module takes its contracts: nothing in
platformkit, the catalog or the clients importsmodules/audit/contracts; the compositions import the manifest, not the contracts. -
The admin and the native shell do not reach it: it registers no
httpx.Resource, so neither the generated screens norGET /api/v1/admin/resourcesknow the trail. A caller withaudit:readreads the two routes.
Verification
-
go test ./modules/audit/…— the fake (TestTheFakeIsAService) and the real service (TestServiceConforms, on a real Postgres in a rolled-back tenant transaction) pass one conformance suite: a row says what happened, who caused it and when; a job’s event is nobody’s; three deliveries of one event leave one row; the filters by name, actor and time, newest first and paged; an unknown id is not found; and nothing is published.TestTheTrailIsTenantOwnedproves another tenant sees nothing, by the policy inmigrations/000010_audit.up.sql;TestRetentionForgetsOnlyWhatIsOldEnoughproves the boundary and the loop’s exit. -
No browser journey:
e2e/admin-tasks.spec.tsdoes not touch the trail. -
Not proven: the two routes, which have no handler test; the batching of the retention delete, whose fixture is smaller than a batch; the expansion of
SubscribeAllagainst a worker and a live transport, which is the kernel’s to prove.