Audit log and telemetry
Status: draft M0. Distinction according to B11. Audit log: 03-data-model.md
4.12, actions with audit: 05-actions.md. Security chapter 19 and
ADR 0021 define the content and the checking of the audit log.
1. Comparison
| Attribute |
Audit log (audit_events) |
Telemetry (traces, metrics, logs) |
| Purpose |
Evidence: who decided or changed what |
Operations: is it running, where does it get stuck |
| Content |
Action, decision, reason, resource, actor (plain ID), device, session |
Durations, counters, error types, IDs of objects, pseudonyms of people |
| Completeness |
Every event of the mandatory list; denials aggregated per window |
Sampled, may be discarded under backpressure |
| Writing |
In the same transaction as the change; if the audit fails, the change fails |
Asynchronous, fail-open |
| Integrity |
Hash chain, only INSERT and SELECT for the application |
None |
| Location |
PostgreSQL of the instance |
Collector, then a backend of choice |
| Access |
Owner with step-up (audit.read, audit.verify) |
Whoever runs the backend |
| Retention |
Own class audit (chapter 20) |
Short, per backend (privacy.md 5) |
| Backup |
With the database, chain checked in the restore test |
None |
| Export |
Only by the owner via exportAuditEvents; the chain is checked with verifyAuditChain (M1-50) |
Per overlay |
2. Rules
- No audit entry is created from telemetry, no telemetry from the audit log. The code writes
both at the same place, separately.
- Link only through
correlation_id: it is the trace ID of the request
(signals.md 5.1). An auditor can jump from the audit entry to the trace as long as
the trace is still retained and recorded.
- Denials by
authorize appear in both: in the audit log as evidence (aggregated by
audit.deny_aggregate_window), in telemetry as the counter bothy.authz.decisions without an
actor.
- The collector never receives the content of
audit_events.details.
- The audit log contains plain IDs of people, telemetry only pseudonyms. Whoever runs telemetry
therefore cannot trace who did something; that is what the audit log is for.
- The check of the hash chain runs periodically in the
worker (bothy.audit.verify.*) and in
every restore test; a failure is an alert page.
3. Example: revoke a grant
| Location |
What is created |
audit_events |
action = grant.revoke, decision = allow, actor, device, session, resource_id of the grant, correlation_id |
event_log |
access.changed to the affected people, if needed run.cancel_requested, same correlation_id |
| Trace |
Span POST /api/v1/grants/{id}/revoke with bothy.action = grant.revoke, bothy.decision = allow, pseudonym of the actor |
| Metric |
bothy.authz.decisions{bothy.action="grant.revoke", bothy.decision="allow"} plus one |