Skip to content

ADR-018: Shared Audit Service with Owner-Managed HTTP APIs ​

Status ​

Accepted

Context ​

Admin and Organization need durable histories of significant changes with the actor and request context. The shared event bus has no transactional outbox, and best-effort publication cannot guarantee a complete history. Provider-owned mutations, authorization transactions, and multi-step provisioning require explicit integration.

Decision ​

Add one services/audit module owning append-only PostgreSQL storage and curated recording/query capabilities. Admin and Organization own all HTTP audit endpoints. Audit itself registers no HTTP routes.

Commit each authoritative change and its audit entry in the same database transaction. Read locked before-state within the transaction. Abort the enclosing transaction on every append failure, including validation failures. Keep existing multi-step provisioning commits and audit each successful step, correlating them by operation ID. Do not use the event bus as audit transport.

Keep Admin and organization histories separate according to the initiating authority. Preserve affected organization separately from visibility scope. Evaluate dedicated scoped audit read permissions and restrict queries in SQL. Capture server-verified actor identity and name snapshots, approved field-level changes, and bounded request context. Capture the transport peer before forwarded-header rewriting; network metadata does not authenticate actors.

Retain history indefinitely, with database triggers rejecting update/delete/truncate. Do not tie historical references to live rows with cascading foreign keys. Trust database administrators; cryptographic verification is outside this version. Guarded development cleanup can temporarily disable only the immutable trigger inside its transaction.

Record successful mutations and significant implemented workflow events. Exclude reads, failed requests, authentication activity, exports, and unimplemented workflows. Suppress effective no-ops and timestamp-only changes; canonicalize permission collections before comparison. Explicit event serializers allow only approved business fields and metadata, excluding credentials, tokens, raw requests, and errors. Generate operation IDs on the server and propagate trusted actor and request context through provider-owned commands and provisioning steps.

History reads require admin.audit.read or org.audit.read in the matching scope; platform grants do not grant organization history access. Lists expose summaries with versioned opaque cursors bound to caller, scope, and filters, ordered by database recording time and ID descending. Detail includes approved changes and context. Scope both list and detail in SQL; cross-scope IDs return 404 after authorization. The Audit service guide records the implemented event coverage, HTTP contracts, filters, and rollout procedure.

Consequences ​

  • An audited write cannot succeed while its history fails to persist.
  • Provider transaction runners and public mutation contracts carry trusted audit context.
  • Identity account provisioning can remain committed when a later membership step fails; both committed steps are individually traceable.
  • Ordinary admins and members need explicit history grants; owners inherit catalog permissions.
  • Records preserve approved personal data indefinitely, including actor names; secrets and raw payloads are excluded.
  • Recording starts at deployment. Existing authentication history is left intact, without fabricated backfill.

Documentation follows the source. Planned contracts are marked explicitly.