ABS Core
Features

Audit Janitor

Automatic log retention and purge. Keep storage costs controlled, satisfy LGPD/GDPR retention requirements, and maintain query performance at scale.

Audit Janitor

Why this exists

Audit logs that accumulate indefinitely create three problems:

  1. Cost: Cloudflare D1 charges per GB stored. A busy agent generating 1,000 decisions/day produces ~365K records/year — each with a full JSON payload.
  2. Performance: SQLite query time degrades as table row count grows without periodic maintenance.
  3. Compliance risk: LGPD and GDPR require data to be deleted once its retention purpose expires. Logs kept beyond the legal period are a liability, not an asset.

The Audit Janitor runs automatically on a daily schedule and purges records older than your configured retention period, with safeguards to never delete records that are still needed.

Default behavior

The Janitor starts automatically when the ABS server starts, with a 5-minute warmup delay. It runs every 24 hours.

Default retention: 90 days. Configure with the ABS_AUDIT_RETENTION_DAYS environment variable.

Tables managed automatically:

TableDefault retentionSafeguard
audit_logs90 daysNever deletes records referenced by pending human reviews
token_usage90 days
budget_violations90 days
cortex_sequences90 days
threat_intel_received7 days (TTL)Enforced by expires_at column

Configuration

Set retention via environment variable:

ABS_AUDIT_RETENTION_DAYS=365     # Keep 1 year (e.g. for SOC2 audits)
ABS_ALERT_WEBHOOK_URL=https://hooks.slack.com/...  # Alert if purge fails

Or configure per-tenant via the API:

PUT /v1/maintenance/retention
Authorization: Bearer {your-api-key}

{
  "tenant_id": "acme-corp",
  "retention_days": 365,
  "alert_webhook_url": "https://hooks.slack.com/services/..."
}

Valid range: 7 – 3650 days.

Check the last run

GET /v1/maintenance/janitor
Authorization: Bearer {your-api-key}
{
  "last_run": {
    "run_at": "2026-02-21T03:00:00Z",
    "retention_days": 90,
    "total_deleted": 12847,
    "duration_ms": 340,
    "success": true,
    "details": [
      { "table": "audit_logs", "deleted": 11200 },
      { "table": "token_usage", "deleted": 1580 },
      { "table": "budget_violations", "deleted": 67 },
      { "table": "cortex_sequences", "deleted": 0 }
    ]
  },
  "retention_config": {
    "default_days": 90,
    "per_tenant": []
  }
}

Trigger a manual purge

Useful before a scheduled compliance audit or after a large bulk import:

POST /v1/maintenance/janitor/run
Authorization: Bearer {your-api-key}

{
  "retention_days": 90
}

Dry run (preview before deleting)

Check how many records would be deleted without actually deleting anything:

POST /v1/maintenance/janitor/run
Authorization: Bearer {your-api-key}

{
  "dry_run": true,
  "retention_days": 90
}
{
  "dry_run": true,
  "retention_days": 90,
  "cutoff": "2025-11-23T00:00:00Z",
  "would_delete": {
    "audit_logs": 8500,
    "token_usage": 920,
    "budget_violations": 12,
    "cortex_sequences": 0
  }
}

View run history

GET /v1/maintenance/janitor/history

Returns the last 30 Janitor runs — useful for confirming the schedule is running and diagnosing failures.

Invariants

  • AJ-I1: Retention period is configurable per tenant. Global default: 90 days.
  • AJ-I2: Audit logs referenced by pending human reviews are never deleted, regardless of age.
  • AJ-I3: Every purge run is logged to audit_janitor_runs for accountability.
  • AJ-I4: If a purge fails, an alert is sent to the configured webhook URL.
  • AJ-I5: The Janitor runs with a 5-minute startup delay to avoid interfering with server initialization.

On this page