Aegis Architecture — Four Defense Layers

Aegis is a defense-in-depth backup, recovery, and time-travel feature built into the Aeion OS platform module. Pre-destructive migration guard, logical snapshots, continuous PITR via pgBackRest, application time travel via the temporal event log — each layer covers a different failure mode. This page documents the architecture, REST endpoints, event surface, and operational characteristics of each defense layer. Target audience: CTOs, CISOs, platform engineers, and security auditors.

Aegis Subsystem Map

PITR Orchestration

pgBackRest orchestration — stanza setup, base backup, restore, status

Backup Verification

Weekly cron — restore latest snapshot to an isolated database + sanity checks

Restore Engine

Logical restore — full DB / schema / tables / collection

Compliance Reports

Monthly cron — SOC 2 / ISO 27001 / GDPR / HIPAA / PCI-DSS reports

Notification Fan-Out

Per-event notification dispatch + rule engine

Bulk Undo

Admin recovery — filter (user / time / collection) → reverse JSON patches

Encryption Key Lifecycle

CMK lifecycle — generate, rotate, legacy-import, audit

Tenant Purge

GDPR right-to-erasure — tenant-scoped purge with audit

Heartbeat Self-Check

Daily dead-man's-switch across every Aegis cron + 5-min PITR status refresh

Layer 1 — Pre-Destructive Migration Guard

How it works:

  1. Before any schema change is applied to your database, Aegis inspects the generated change set for anything destructive — dropped tables, dropped columns, or truncates.
  2. For each affected table, Aegis counts the live rows that would be lost.
  3. If any non-empty table would be affected:

The guard never auto-overrides itself — a destructive change always requires an explicit, logged decision before it can proceed, and the automatic snapshot means an override is always undoable.

Layer 2 — Continuous PITR (pgBackRest)

Architecture. PITR runs on a pluggable backend — the production path wraps pgBackRest directly, an external option targets customer-managed Postgres + pgBackRest, and there's an honest fallback for when PITR isn't configured at all (it reports itself as unavailable rather than pretending to protect you).

pgBackRest configuration (generated by Aegis for you):

``` [global] repo1-type=s3 repo1-s3-endpoint=s3.us-east-1.amazonaws.com repo1-s3-bucket=your-aegis-bucket repo1-s3-region=us-east-1 repo1-cipher-type=aes-256-cbc repo1-cipher-pass=<securely generated and stored> compress-type=zst compress-level=3 archive-async=y archive-push-queue-max=4GiB

[aeion] pg1-path=/var/lib/postgresql/16/main ```

`postgresql.conf` edits (applied automatically):

wal_level = replica archive_mode = on archive_command = 'pgbackrest --stanza=aeion archive-push %p' archive_timeout = 60 # forces a switch every 60s under low write load

Schedule. Base backups run every 6 hours by default; status refreshes every 5 minutes.

Events emitted:

  • aegis.pitr.base_backup_completed
  • aegis.pitr.base_backup_failed
  • aegis.pitr.status_refreshed
  • aegis.pitr.archive_lag_high (when WAL lag exceeds threshold)
  • aegis.pitr.recovery_window_shrunk (when retention rolls forward)

Status endpoint response (GET /api/v1/platform/aegis/pitr/status):

json { "configured": true, "streaming": true, "lastBaseBackupAt": "2026-05-30T06:00:00Z", "lastBaseBackupSizeBytes": 52428800000, "archiveLagSeconds": 12, "archiveFailureCount24h": 0, "recoveryWindowStart": "2026-05-01T00:00:00Z", "recoveryWindowEnd": "2026-05-31T09:12:00Z", "lastVerificationAt": "2026-05-29T03:00:00Z", "lastVerificationResult": "passed", "nextScheduledBaseBackupAt": "2026-05-31T12:00:00Z" }

Layer 3 — Logical Snapshots + Restore

Architecture. The restore engine pairs with the backup pipeline — backups produce snapshots, and the restore engine consumes them for full-DB, schema, table, or single-collection restores.

Backup pipeline. Aegis runs a standard pg_dump, compresses it, and streams it directly to your S3-compatible bucket. Each snapshot job moves through a simple state machine:

pending → running → completed ↓ failed

Restore modes:

  • full — drop+recreate the database (an automatic rollback snapshot is taken first)
  • schema — restore a single schema
  • tables — restore one or more specific tables
  • collection — extract one collection's table + its associated indexes
  • pitr_in_place — Layer 2 path, restoring point-in-time directly against the live data directory
  • pitr_sidecar — Layer 2 path, restoring into a temporary, isolated Postgres instance instead

Restore job states:

pending → running → completed ↓ failed → rolled_back (auto-rollback via the pre-restore snapshot)

Pre-restore preview — before applying a restore, Aegis previews exactly what will change (which tables, how much data) without touching your live data. This powers the "what will change?" step in the restore wizard.

S3 cleanup happens automatically — old backup job records and their corresponding S3 objects are deleted together, so orphaned files never accumulate in your bucket.

Layer 4 — Application Time Travel (Temporal DB)

Every write to a temporal-enabled collection is intercepted automatically and recorded as a reverse patch — a compact record of exactly how to undo that change. Point-in-time document reconstruction replays those patches against the current state to produce what the document looked like at any past moment.

Each event records which document changed, who changed it, when, and the reverse patch needed to undo it — indexed for fast per-document lookup.

Reconstruction algorithm:

  1. Find the latest version of the document AT OR BEFORE the target timestamp.
  2. Apply reverse patches in newest-to-oldest order to produce the historical state.
  3. If the document didn't exist at the target time, return null.

QueryBuilder API:

typescript const post = await ctx.services["posts"] .query(ctx) .where({ id: "post_abc" }) .asOf("2026-05-15T10:30:00Z") .first();

A matching per-document HTTP route factory (reconstruct / history / timeline / compare) ships in @aeion/core, ready for any module to mount — the QueryBuilder .asOf() API above is the primary, currently-wired way tenants and modules reach this capability today.

Bulk Undo — filter by user / time / collection, reverse in one call:

POST /api/v1/platform/aegis/bulk-undo/execute { "tenantId": "t1", "filter": { "userId": "agent_x", "timeRange": { "from": "2026-05-18T09:00:00Z", "to": "2026-05-18T09:15:00Z" }, "collections": ["contacts", "deals"] } } // → { "reverted": 87, "errors": [], "elapsedMs": 2483 }

The same filter can be sent to POST /api/v1/platform/aegis/bulk-undo/preview first to see what would be reverted without applying anything.

Bulk Undo coordinates cross-collection consistency — if reverting a delete on deals requires recreating a deleted contacts row, reversals are ordered correctly.

Retention — historical event data is purged automatically beyond your configured retention window. Defaults to 365 days; tunable per collection.

Notification Surface — 34 Events

EventSeverityChannel default
`aegis.pitr.base_backup_completed`infolog
`aegis.pitr.base_backup_failed`criticalpage
`aegis.pitr.archive_lag_high`warningemail
`aegis.pitr.recovery_window_shrunk`warningemail
`aegis.pitr.status_refreshed`infolog
`aegis.pitr.bootstrap.started`infolog
`aegis.pitr.bootstrap.configured`infoemail
`aegis.pitr.bootstrap.skipped`infolog
`aegis.pitr.bootstrap.failed`criticalpage
`aegis.pitr.restore.dispatching`infolog
`aegis.pitr.restore.started`infolog
`aegis.pitr.restore.snapshot_taken`infolog
`aegis.pitr.restore.snapshot_skipped`infolog
`aegis.pitr.restore.completed`infoemail
`aegis.pitr.restore.failed`criticalpage
`aegis.pitr.extract.started`infolog
`aegis.pitr.extract.completed`infolog
`aegis.pitr.extract.failed`warningemail
`aegis.pitr.verification_passed`infolog
`aegis.pitr.verification_failed`criticalpage
EventSeverityChannel default
------------------------------------------------
`aegis.restore.completed`infoemail
`aegis.restore.failed`criticalpage
EventSeverityChannel default
----------------------------------------------------------
`aegis.verification.passed`infolog
`aegis.verification.failed`criticalpage
`aegis.verification.skipped`infolog
`aegis.compliance.report.generated`infoemail
`aegis.compliance.report.failed`warningemail
`aegis.compliance.report.skipped`infolog
`aegis.bulk_undo.completed`infoemail
`aegis.cron.missing`warningemail
EventSeverityChannel default
-------------------------------------------------------------
`aegis.encryption.key.generated`infoemail
`aegis.encryption.key.rotated`infoemail
`aegis.encryption.key.legacy_imported`warningemail
`aegis.tenant.purged`warningemail

What Aegis Tracks

Aegis maintains a full operational record of everything it does, so nothing about your backup and recovery posture is a black box:

  • Storage configuration — your BYOB provider settings, with credentials encrypted at rest
  • Backup + restore history — every snapshot and restore job, its status, and rollback tracking so any restore can be undone
  • Verification results — every weekly verification run, pass/fail, and measured RTO
  • Compliance report archive — every generated monthly report
  • PITR setup + status — bootstrap progress and live pipeline health
  • Encryption key audit trail — key generation, rotation, and import events (metadata only — the plaintext key itself is never stored)
  • Version history retention audit — what was purged under Layer 4 retention, when, and why
  • GDPR erasure audit log — a full record of right-to-erasure purges

REST API Surface

MethodPathPurpose
POST`/api/v1/platform/backup/trigger`Trigger a manual snapshot
GET`/api/v1/platform/backup/jobs`List recent backup jobs
GET`/api/v1/platform/backup/jobs/:id`Get one job
POST`/api/v1/platform/backup/config/test`Validate storage credentials (HeadBucket + RW)
POST`/api/v1/platform/aegis/restore/preview`Preview a logical restore before applying it
POST`/api/v1/platform/aegis/restore`Trigger a logical restore
GET`/api/v1/admin/backup/restore-points`List available snapshots + PITR window
GET`/api/v1/platform/aegis/pitr/status`Read current PITR pipeline status
GET`/api/v1/platform/aegis/pitr/recovery-window`Detailed PITR availability breakdown
POST`/api/v1/platform/aegis/pitr/restore`PITR restore (in-place or sidecar)
POST`/api/v1/platform/aegis/pitr/take-base-backup`Manual trigger of pgBackRest base backup
POST`/api/v1/platform/aegis/pitr/verify`Manual pgBackRest `verify` invocation
POST`/api/v1/platform/aegis/verification/run-now`Trigger an on-demand verification run
GET`/api/v1/aegis_compliance_reports`List generated compliance reports
POST`/api/v1/platform/aegis/compliance/run-now`Trigger an on-demand compliance report
POST`/api/v1/platform/aegis/bulk-undo/preview`Preview a bulk-undo filter before reverting
POST`/api/v1/platform/aegis/bulk-undo/execute`Filter + reverse Temporal events

Heartbeat + Self-Check

Aegis runs a daily dead-man's-switch: alerts that fire when a cron _runs_ are useful, but if a cron silently stops running, that gap otherwise surfaces only during an audit. Once a day, the heartbeat checks the last-run timestamp of every known Aegis cron against its expected freshness window (about 1.5× the cron's own interval):

  1. PITR status refresh — expected every 5 minutes, flagged if stale beyond 15 minutes
  2. Temporal retention/archive sweep — expected daily, flagged if stale beyond 36 hours
  3. Weekly backup verification — expected weekly, flagged if stale beyond 8 days
  4. Monthly compliance report generation — expected monthly, flagged if stale beyond 32 days

Per-tenant crons are skipped honestly for tenants that haven't configured BYOB backups — an unconfigured tenant has no stale cron to flag, just an honest no-op. If any cron is stale, Aegis emits aegis.cron.missing naming the cron and how long it's been silent. The default notification rule routes it to email at warning severity; you can tune this in your own notification rules.

Operational Invariants

Aegis never sees plaintext under CMK

Client-side AES-256-GCM encryption happens before your data ever leaves your host. Neither pgBackRest's cipher key nor your snapshot encryption key is ever sent to Aeion's servers.

Restore jobs auto-snapshot pre-execution

Any restore mode automatically takes a rollback snapshot before making changes — referenced by the one-click "Undo Restore" button in the Aegis Hub.

Verification failures flip compliance controls to `unmet`

No human can mark a control as passing — the monthly report is generated directly from real verification results. If verification failed, the report says so.

Layer 1 never silently overrides itself

A destructive change always requires an explicit, logged human decision before it can proceed.

A stopped cron can't hide

The daily dead-man's-switch checks every Aegis cron's last-run timestamp independently of the alerts those crons themselves emit — so a cron that silently stops running is caught even though it can no longer report on itself.

Architecture Review or Onboarding Help?

Composes with the rest of the OS