Architecture
Audit
An audit trail exists to answer for the system when someone disputes what happened. The framework treats that as part of the application contract: security- and business-relevant actions produce structured, append-only, queryable records — separate from diagnostic logging, and built to outlive it.
Audit is a contract, not logging
Logs are transient and swappable. They flow through a pluggable transport with its own retention, and a deployment can change where they go and how long they live without touching anything the application promises. Audit is different in kind: it is append-only data, with no update or delete path in the application. It is produced because the action demands a record, not as a side effect of how the service happens to be operated.
The two meet in exactly one place. Every request carries a correlation identifier shared by its log lines, its audit record and the events it publishes, so a single action can be followed across all three. Each keeps the lifetime appropriate to its job. Events leave the API for your event infrastructure carrying that same identifier, so your own tooling can join them back to the action that raised them.
What a record answers
Every audited action produces one structured record, built to answer the questions a dispute actually asks.
- Who. The actor, always with a human-readable name — including system and integration actors. “A process did it” is never the whole answer.
- What changed. A field-level before-and-after diff, scrubbed by the same sensitive-field deny-list that protects API responses and published events, so a secret cannot land in the audit trail.
- Where and when. The tenant the action happened in, timestamped with ordering that stays stable across application instances.
- Why. A human-supplied justification, on the routes that demand one.
The enforced “why”
On routes that require a justification, it is not a courtesy field: a change submitted without one is rejected outright. A disputed action nobody can explain is one of the two expensive failures this architecture exists to prevent; the other is cross-tenant disclosure. So the explanation is captured at the moment of the change, not reconstructed later from memory and log fragments.
Guaranteed when it matters
How strongly a record is guaranteed is chosen per route. Where certainty matters, the audit record is written inside the same database transaction as the change it describes: a committed change cannot lack its record, and a failed audit write rolls the change back. Routes where that trade-off is not warranted can let the record follow the commit instead. Requiring a justification implies the transactional guarantee by default — if the “why” is worth enforcing, the record is worth guaranteeing.
Queryable, deliberately strict
The trail is queryable through a search interface that is strict on purpose. Queries are tenant-scoped, one tenant at a time, and run over bounded time windows, so audit never becomes a bulk-export or cross-tenant surface. Following a single record’s full history is the deliberate exception. System records — actions that belong to no tenant — sit behind a separate permission. The strictness is the point: the trail answers precise questions without becoming a second way to read business data.
Built for retention
Audit lands in an append-only store, and storage scales through profiles, from a plain table up to partitioning by tenant and month. A long retention obligation does not weigh on the working data. Retention itself is policy: audit outlives diagnostic logs, because logs answer this month’s operational question and audit answers next year’s dispute. That in turn keeps log retention an operational choice rather than a compliance one.
Access changes are audited too
The same architecture covers changes to access itself. Access Management records every grant, role and provisioning change on the same audit foundation as the data plane, and every use of platform capability is alerted as it happens rather than discovered afterwards. Who can act where, and since when, is an answerable question — held to the same standard as any other record.