Raxx · internal docs

internal · gated

SC-A13 Audit Archiver Runbook

Issue: #1493 | Job: backend_v2/jobs/audit_archiver.py

Overview

The SC-A13 archiver job exports audit rows to S3 on a monthly (or on-demand) schedule and prepares the legacy audit_log tables for the Phase 4 drop:

  1. Unified archival — customer_audit_events rows older than the retention threshold (default 7 years, SOC-2) are exported to S3 Glacier and then deleted from Postgres, using the audit_archiver role (SELECT + DELETE only — see backend_v2/alembic/versions/0056_audit_archiver_grants.py).
  2. Legacy archival — all rows from Raptor and Console audit_log are exported to S3 Standard storage (immediately queryable) as a backup, ahead of the Phase 4 legacy-table drop. Legacy rows are not deleted by this job.

Prerequisites

  1. AUDIT_ARCHIVER_DATABASE_URL set to a DSN authenticated as the audit_archiver Postgres role (production). Without this, the job falls back to the default Raptor engine and logs a warning — acceptable in dev/test only.
  2. CONSOLE_DATABASE_URL set for Console legacy archival (optional — skipped with a warning if absent).
  3. S3_BUCKET_AUDIT_ARCHIVE set (default raxx-audit-archive).
  4. The audit_archiver Postgres role must already exist with the grants from migration 0056 applied.

Running the job

# One-time bucket bootstrap (idempotent; Object-Lock eligible, WORM NOT activated)
python -m jobs.audit_archiver --create-bucket --tables unified --dry-run

# Full monthly run
python -m jobs.audit_archiver

CLI flags:

Flag Default Effect
--dry-run off Compute batches/metadata; no S3 upload, no DELETE, no manifest row
--tables unified legacy_raptor legacy_console Subset to process
--batch-size 10000 Rows per S3 export batch
--retention-years 7 Unified-table cutoff (SOC-2)
--create-bucket off Idempotently create the S3 bucket first

S3 path structure

Unified (Glacier):  s3://<bucket>/unified/customer_audit_events/<year>/<month>/<run_id>-batch<NNNN>.json
Legacy  (Standard):  s3://<bucket>/legacy/<table>/<year>/<month>/<run_id>-batch<NNNN>.json

<table> for legacy exports is audit_log_raptor or audit_log_console. Objects are newline-delimited JSON (application/x-ndjson).

Object Lock (WORM)

--create-bucket creates the bucket with ObjectLockEnabledForBucket=True (eligible), but applies no default retention or legal-hold configuration. WORM activation for SOC-2 audit readiness is a Phase 2 follow-up card — do not activate it as part of this job.

Manifest

Every table processed in a run writes one row to audit_archival_runs with run_type = 'cold_export' (the value reserved for SC-A13 by migration 016). manifest_path holds the S3 prefix for that table; error_detail is a JSON blob with table, date_range_start, date_range_end, and error (null on success).

SELECT run_type, status, rows_exported, rows_deleted, manifest_path, error_detail
FROM audit_archival_runs
WHERE run_type = 'cold_export'
ORDER BY started_at DESC
LIMIT 10;

Operator test query (post legacy-archival sign-off)

After a legacy archival run, confirm the S3 archive is queryable before proceeding toward the Phase 4 drop. Using the AWS CLI:

# List objects written by the run
aws s3 ls s3://raxx-audit-archive/legacy/audit_log_raptor/ --recursive

# Fetch one object and spot-check a row
aws s3 cp s3://raxx-audit-archive/legacy/audit_log_raptor/<year>/<month>/<key> - | head -n 1 | python3 -m json.tool

Legacy exports use STANDARD storage class specifically so this query works immediately — no Glacier restore delay. Sign off in the SC-A13 issue/PR comment once a spot-checked row matches the source table.

Retrieval SLA (unified / Glacier)

Unified rows are exported to Glacier and then deleted from Postgres. Retrieval for an audit investigation requires an S3 Glacier restore request, which can take hours. Prefer customer_audit_events for recent data (last 7 years, still live) — Glacier retrieval is a break-glass path for investigations that predate the retention window.