# Backups and per-tenant restore drill

## What is backed up

| What | How | Retention |
|---|---|---|
| Master DB | Managed MySQL automated snapshots + binlog (PITR) | 14 days PITR, 35 daily snapshots |
| Each tenant DB | Nightly logical dump per tenant (`mysqldump --single-transaction`), encrypted with `BACKUP_KMS_KEY_ID`, uploaded to `BACKUP_S3_BUCKET/tenants/{tenant_id}/{date}.sql.gz` | 35 days |
| Media | S3 versioning + replication | 90 days of noncurrent versions |
| Secrets | AWS Secrets Manager (versioned) | provider default |

Each run is recorded in master `backup_runs` (type `tenant_logical`, status, size, sha256 checksum) and shown in Super admin → Operations.

## Per-tenant restore drill (quarterly, staging)

Goal: restore one tenant's DB from last night's dump into a scratch database and prove the app can serve it, without touching other tenants.

1. Pick a tenant `{slug}`; note `tenant_database_connections.database_name` (e.g. `social_tenant_0042`).
2. Download and verify the dump: `aws s3 cp …/{date}.sql.gz .` then check `sha256sum` against `backup_runs.checksum`.
3. Restore into a scratch DB with the provisioner account:
   `mysql -u app_provisioner -p -e 'CREATE DATABASE social_tenant_0042_restore'`
   `gunzip -c {date}.sql.gz | mysql -u app_provisioner -p social_tenant_0042_restore`
4. Point a copy of the tenant connection at the scratch DB: `php artisan tinker` →
   update `tenant_database_connections.database_name` for a **staging clone tenant** (never the live row), then `php artisan tenants:verify --tenant={clone-slug}`.
5. Smoke test as the owner: posts, calendar, analytics, inbox load; counts match the source (`SELECT COUNT(*)` on posts, post_targets, clients).
6. Record: dump age, restore duration, verify output, row-count diff (must be 0), operator, date → insert a `backup_runs` row with type `restore_test` and attach notes below.
7. Drop the scratch DB.

Pass: `tenants:verify` passes, row counts match, restore time < 30 min for the largest tenant.

**Status:** procedure documented in M6; the first drill must be run on staging infrastructure (not available in the development environment).

## Tenant data export vs. backup

`Settings → Your data` produces a portable JSON export for the customer (DPDPA portability). It is not a backup and cannot be restored automatically; use the logical dumps above for recovery.
