Skip to content

Audit retention

Every state-changing action in Ampora produces an audit event. Audit events live in three tiers:

  1. Hot table (audit_events) — fast queries, full structured before / after JSON, indexed by tenant + entity.
  2. Archive table (audit_event_archive) — same shape, no non-essential indexes, optimised for storage.
  3. Purged — the row is gone for good.

The audit retention service runs on the leader instance, sweeps both tables, and moves rows between tiers based on configured windows.

Settings

AuditRetention:
  HotDays: 90              # default: 90
  ArchiveDays: 2555        # default: 7 years
  SweepIntervalMinutes: 60
Key Effect
HotDays Rows older than this in the hot table are moved to the archive
ArchiveDays Rows older than this in the archive table are deleted
SweepIntervalMinutes How often the leader runs a sweep

Both windows are inclusive — a row aged exactly HotDays is kept hot for one more sweep, then moved.

Reading archived events

The audit log UI only queries the hot table by default. To search archived events, the page has an "Include archive" toggle:

  • The toggle is gated by Admin role.
  • Searches with the toggle on are slower (no per-actor index on the archive table) — they typically take a second or two for a year-wide query.
  • The audit log of the audit log: opening the archive emits its own audit event, with the query parameters.

API users can query the archive directly via the ?includeArchive=true query parameter on /api/audit/events.

Compliance presets

Compliance regime Recommended HotDays Recommended ArchiveDays
SOC 2 (typical) 90 365
HIPAA 30 2190 (6 years)
PCI-DSS 90 365
GDPR 30 as long as the lawful basis lasts
ISO 27001 90 1095 (3 years)

These are starting points, not legal advice. Confirm the actual window your auditor expects.

What is logged

Every of these events is recorded with actor, timestamp UTC, entity ID, and a before / after JSON snapshot:

  • Authentication — login, logout, role assumption.
  • Configuration — create, edit, publish, archive.
  • Rollout — create, start, pause, resume, abort, rollback.
  • Group — create, edit, member add/remove, soft-delete, restore.
  • Token — issue, revoke, redeem, pool create, pool admin secret rotate.
  • Identity — issue, revoke, hard-delete (only for Unbound).
  • Policy — create, edit, publish (with approver), retire.
  • PKI — signing-key create, activate, retire; CRL publish; OCSP signer rotate.
  • Federation — peer create, edit, delete, ping, every aggregator call (origin and target).
  • GitOps — source create, sync run start/end, per-file errors.
  • Tenant — create, theme change, identity-mapping change.

What is not logged

  • Read-only queries to the application itself. The Postgres audit extension can record those if you need it; Ampora does not by default.
  • Telemetry payloads flowing through agents — Ampora is not an APM.
  • OpAMP heartbeats — too high-volume to be useful in an audit log. They are summarised as "agent connected for X minutes" via the agent status history instead.

Performance

The retention sweep is designed to be invisible:

  • Each sweep moves rows in batches (default 5 000) with a small commit pause between batches.
  • Both tables are partitioned-friendly; if your cluster grows past the comfortable single-table size, the migration to native partitioning is documented in ADR-034.
  • Indexes on the archive table are minimised on purpose. Heavy archive searches should be rare; if they are not, add the index on the specific column you query and accept the storage cost.

Tamper-evidence

Audit rows are append-only. The application never updates an audit event in place; corrections are new audit events that reference the original. There is no UI or API to mutate audit_events rows.

For deployments that need cryptographic tamper-evidence (each row signing the previous row's hash), ADR-034 documents the integration with RFC-3161 timestamping (ADR-046). It is an opt-in for regulated customers.