Aegis — From Zero to "Verified Backup" in Under an Hour.

Aegis ships pre-configured with Layer 1 (destructive guard) and Layer 4 (application time travel) enabled. The Layer 2 (PITR) and Layer 3 (snapshots) layers need a one-time BYOB storage configuration. This runbook walks through every step from "I want backups" to "my auditor has a SOC 2 control evidence file."

Step 0 — What's Already Working Out of the Box

Layer 1 (Pre-Destructive Migration Guard) is on from the moment your Aeion workspace exists. If a schema change — yours or ours — would ever drop a column or table that still has data in it, you'll see a warning like this before it can proceed:

``` ❌ Aegis Guard: this change would DROP column "internal_notes" on your Contacts collection (4,247 rows would be affected).

To proceed, you'll need to explicitly confirm. An automatic safety snapshot is taken first, so the change can always be undone. ```

There's no CLI flag to memorize and no way to silently skip this — a destructive change always requires an explicit, logged confirmation, and the automatic snapshot means you're never one accidental confirm away from unrecoverable loss.

Layer 4 (Application Time Travel) is enabled per collection with a simple toggle when you set it up. Reads via .asOf(timestamp) are live; Bulk Undo is available as an admin tool with filter-by-user / filter-by-time / filter-by-collection options; the Version History UI is mounted on every temporal-enabled collection view. Old history is purged automatically according to your retention settings, and every purge is logged for audit purposes.

You don't need to do anything for these — they're on. The rest of this runbook covers Layers 2 + 3 (snapshots and PITR), which need an S3 bucket.

Step 1 — Provision a BYOB S3 Bucket

Aegis writes snapshots + WAL to your S3-compatible bucket. Any of these work:

  • AWS S3 — cheapest at scale
  • Cloudflare R2 — zero egress, great for restore drills
  • Backblaze B2 — zero egress, lower storage than AWS
  • Wasabi — fixed-price storage, no egress fees
  • DigitalOcean Spaces — bundled if you're already on DO
  • MinIO — self-hosted (e.g., dedicated backup server)

Required permissions (IAM policy attached to a dedicated Aegis user):

json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::your-aegis-bucket", "arn:aws:s3:::your-aegis-bucket/*" ] } ] }

Best practice — enable Object Lock at bucket creation. Object Lock makes backups immutable for a configurable retention window — even if an attacker compromises your IAM keys, they can't delete recent backups. Aegis doesn't require Object Lock to function, but you can't enable it after the fact in S3, so do it now.

Step 2 — Configure Storage in the Aegis Hub

Navigate to /admin/platform/aegis and open the Storage tab.

Click Add Storage Provider and fill in:

  • Provider: AWS S3 / Cloudflare R2 / Backblaze B2 / MinIO / Other (S3-compatible)
  • Endpoint URL: e.g. https://s3.us-east-1.amazonaws.com (AWS) or https://<account-id>.r2.cloudflarestorage.com (R2)
  • Bucket: your-aegis-bucket
  • Access Key: AKIA...
  • Secret Key: <encrypted at rest>
  • Region: us-east-1 (ignored by R2/B2 but required by AWS)

Hit Test Connection. Aegis runs a real HeadBucket check + a 1-byte read/write/delete round-trip. You'll see one of:

  • Connection verified — bucket exists, credentials valid, write+delete permitted
  • 403 Forbidden — missing s3:PutObject — your IAM policy is too narrow; check the JSON above
  • Bucket does not exist or region mismatch — fix the bucket name or region
  • Network timeout — endpoint URL or firewall

Once green, click Save. The storage provider is now wired.

Step 3 — Take Your First Manual Backup

Don't trust the cron yet. Go to the Snapshots tab and click Backup Now.

A new backup job appears with status: pending → running → completed. Watch the live progress bar. For a 50 GB DB this runs in ~3-5 minutes. Output is a single .sql.gz file pushed to your bucket at:

s3://your-aegis-bucket/snapshots/{tenant}/{YYYY}/{MM}/{DD}/full-{job-id}.sql.gz

Hit Verify Backup. Aegis downloads the snapshot, restores it into a temporary, isolated database on the same host, runs row-count anomaly checks against the live DB, and tears the temporary database down. Look for pass: true and a measured RTO under 10 minutes on a 50 GB DB.

If the verification step fails — stop. The most common cause is a mismatch between the version Aegis ran against and the destination Postgres version. Fix that before relying on the cron.

Step 4 — Schedule Automatic Daily Snapshots

Open the Schedule tab. Set:

  • Frequency: Daily / Weekly (Daily recommended for production)
  • Time: 03:00 UTC (off-peak; tune for your tenant's traffic pattern)
  • Retention: 30 days (default; configurable per tenant)
  • Format: Plain SQL (default, compatible across Postgres versions) or Custom format (faster restore)

Monitor snapshot health from the Snapshots tab, or automate it via the API. You can check job status any time in the Aegis Hub, or poll GET /api/v1/platform/backup/jobs for automated monitoring. For real-time alerting without polling, enable Layer 2 PITR (Step 5 below) — it emits aegis.pitr.base_backup_completed / aegis.pitr.base_backup_failed events that route to email, Slack, PagerDuty, or a webhook via your notification rules. We recommend a high-priority alert on aegis.pitr.base_backup_failed immediately — silent backup failure is the #1 cause of "we thought we had backups."

The Aegis heartbeat runs every 5 minutes and emits aegis.cron.missing if any expected cron (archive, retention, verification) hasn't fired in its expected window. Wire a critical-priority rule on aegis.cron.missing — this catches scheduler drift before it becomes a recovery problem.

Step 5 — Enable Continuous PITR

PITR brings RPO from 24 hours to ~30 seconds. Open the PITR tab.

One-time bootstrap runs the pgbackrest stanza-create workflow against your bucket. This takes 30-60 seconds and writes the initial repository skeleton to s3://your-aegis-bucket/pitr/{stanza-name}/. Aegis edits your Postgres config to enable WAL archiving:

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

Aegis reloads Postgres (SELECT pg_reload_conf()) — restart only required if wal_level was previously below replica. After reload, WAL begins streaming to your bucket. The PITR tab now shows the live recovery window — the range of seconds you can rewind to.

After ~10 minutes of WAL accumulation, run Take Base Backup. This captures a full snapshot that future PITR restores will replay WAL forward from. Aegis schedules base backups every 6 hours by default — shorter intervals = faster restores but more bucket storage.

Step 6 — Schedule Weekly Verification Cron

The verification cron is your auditor's favorite line item. It runs row-count anomaly + foreign-key validity + schema-hash checks against a restored sandbox copy. Open the Verification tab and set:

  • Frequency: Weekly (recommended) / Monthly
  • Day & time: Sundays at 04:00 UTC works for most tenants
  • Test scope: Latest snapshot + most recent PITR restore-point
  • Notification on failure: Critical priority — page immediately

Each run records:

  • passed: true|false
  • rtoSeconds — measured restore time
  • rowCountDelta — verification table sanity check
  • errors: [] — anything that didn't replay cleanly

Events emitted:

  • aegis.verification.passed — success path, info severity
  • aegis.verification.failed — failure path, critical severity (page immediately)
  • aegis.verification.skipped — verification didn't run (no recent backup to verify against)
  • aegis.pitr.verification_passed / aegis.pitr.verification_failed — PITR-specific verification via pgbackrest verify

If a verification fails, the monthly compliance report flips the SOC 2 CC7.1 control to unmet until a successful run lands. This is intentional — auditors trust evidence, not promises.

Step 7 — Schedule Monthly Compliance Reports

Open the Compliance tab. Toggle on the frameworks relevant to your audit:

  • SOC 2 Type II — most common for SaaS
  • ISO 27001 — international
  • GDPR Article 32 — if you process EU personal data
  • HIPAA § 164.308(a)(7) — for health-module tenants (BAA required)
  • PCI-DSS Req. 9.5 — for payment processors

On the first of each month, Aegis assembles an HTML compliance report covering the past 90 days of:

  • Snapshot success rate
  • Verification run results (with measured RTO)
  • Retention policy + Object Lock status
  • Encryption mode (S3-SSE / CMK / unencrypted — yes, we'll flag unencrypted)
  • Control mapping per framework

The report is saved to your account and uploaded as a self-contained HTML file to s3://your-aegis-bucket/compliance-reports/. Events emitted:

  • aegis.compliance.report.generated — success, info severity, email channel default
  • aegis.compliance.report.failed — warning severity, email channel
  • aegis.compliance.report.skipped — informational (e.g., not enough data in the window)

Email + Slack notification when ready. Print to PDF, attach to your audit response.

Step 8 — Optional: Customer-Managed Key (CMK) Encryption

For zero-trust posture, enable CMK in the Security tab. Aegis manages the full key lifecycle — generation, rotation, legacy import, and audit. You provide a 32-byte AES-256 key (Aegis generates one for you if you don't have one). Aegis keeps a full audit trail of key generation, rotation, and import events — the plaintext key itself is never stored. All snapshots + WAL are client-side encrypted before upload. Aeion never sees the plaintext.

Events emitted for the CMK lifecycle (info severity by default; bump to critical if your threat model warrants):

  • aegis.encryption.key.generated — new key minted
  • aegis.encryption.key.rotated — scheduled or manual rotation
  • aegis.encryption.key.legacy_imported — warning severity (importing an externally-managed key brings the same recovery risk as any CMK)

Critical: If you lose the CMK, your backups become permanently unrecoverable. Aegis never stores your plaintext key — only a fingerprint, used to confirm you're supplying the correct key back at restore time. We can't decrypt for you. Your options:

  1. Store the key yourself in a secrets manager you control (AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault) or a password manager, and supply it whenever Aegis needs it for a backup or restore. Build this into your operational runbook — treat it with the same rigor as a root credential.
  2. Don't enable CMK if you don't have a key-management story. S3-SSE (server-side encryption by the storage provider) is still strong for most threat models.

Operator FAQ

20-30 minutes if you already have an S3 bucket and IAM credentials. 60-90 minutes if you need to provision them. PITR bootstrap adds another 30 minutes (mostly waiting for the first base backup to complete).

Snapshots (Layer 3) are version-agnostic — plain `pg_dump` works across versions with a compatible `pg_restore`. PITR (Layer 2) is more version-sensitive; the pgBackRest stanza is tied to a specific major version. On a major-version upgrade, run `pgbackrest stanza-upgrade` yourself against your bucket before resuming archiving — this step isn't automated from the Hub today.

Yes — the **Sidecar Restore** option (included) spins up a separate, temporary Postgres instance, restores the snapshot or PITR target into it, and runs your queries against that instance. Production is untouched. Drop the sidecar with one click when done.

Negligible under normal load. WAL segments are 16 MB by default; under low write load you'll see one segment every few minutes. Under heavy write load (1000 TPS+) expect a steady stream of ~5-10 MB/sec to your bucket. Use Cloudflare R2 or Backblaze B2 to avoid egress charges.

Storage tab → **Rotate Credentials**. Provide the new credentials, hit **Test**, save. The old key is held in the audit log (encrypted) for 7 days in case you need to roll back. The PITR pipeline doesn't restart — the new credentials are picked up on the next archive-push.

Yes, but each must use a distinct prefix (e.g. `s3://your-bucket/aegis-prod/`, `s3://your-bucket/aegis-staging/`). Aegis enforces the prefix per-deployment in the storage config.

Aegis is stateless — recovery doesn't depend on the Aeion control plane. You can run `pgbackrest restore` directly with the credentials you configured: `pgbackrest --stanza=aeion --type=time --target='2026-05-18 10:30:00+00' restore`. The same artifacts work on any Postgres host — you are never locked into Aeion's own tooling to get your data back.

Ready to Run Your First Backup?

Composes with the rest of the OS