# Backup and recovery guide

## Required backup set

A recoverable PowerAMS backup consists of the MySQL database, `storage/uploads`, the populated `.env` from the secret store, branding/private-file metadata, and the exact application release identifier. Database and uploads must be captured as one coordinated recovery point.

Use encrypted storage, separate backup credentials, immutable retention where available, off-site replication, checksum verification, and quarterly restore exercises. A backup is not successful until it can be read and its checksum matches.

## Database backup

For a transactional online backup on a single MySQL host:

```bash
umask 077
mysqldump --defaults-extra-file=/etc/powerams/backup.cnf \
  --single-transaction --quick --routines --triggers --events \
  --set-gtid-purged=OFF --hex-blob powerams \
  | gzip -9 > /srv/backups/powerams/powerams-YYYYMMDD-HHMMSS.sql.gz
sha256sum /srv/backups/powerams/powerams-YYYYMMDD-HHMMSS.sql.gz \
  > /srv/backups/powerams/powerams-YYYYMMDD-HHMMSS.sql.gz.sha256
```

Do not put a password on the command line. `/etc/powerams/backup.cnf` must be root-readable only. For large databases, use a physical MySQL backup tool with point-in-time binlog recovery.

## Private-file backup

Snapshot or archive `storage/uploads` without following untrusted links. Preserve generated names, permissions, and directory structure. Original upload names are database metadata only. Never copy private documents into `public/` for backup convenience.

After both database and file verification succeed, write `storage/backups/latest-success.json` with backup ID, completion UTC time, database checksum, upload snapshot ID, and release ID. The health command uses this marker for monitoring; the marker is not itself a backup.

## Restore procedure

1. Declare an incident, stop inbound writes, and preserve logs/evidence.
2. Select a matched database/upload recovery point and verify checksums.
3. Provision an isolated MySQL 8 database and compatible application release.
4. Restore the database:

```bash
gunzip -c powerams-YYYYMMDD-HHMMSS.sql.gz | mysql --defaults-extra-file=/etc/powerams/restore.cnf powerams_restore
```

5. Restore uploads outside `public/` with directories `0750` and files `0640`.
6. Configure fresh runtime secrets. Revoke or rotate API tokens, remembered-login tokens, webhook secrets, and database credentials if compromise is suspected.
7. Run `php cli migrate:status`; do not migrate until the recovered release/schema pairing is confirmed.
8. Run `php cli health:check`, application tests, tenant-isolation smoke checks, and attachment downloads through authorized routes.
9. Reopen traffic gradually and monitor errors, failed jobs, audit logs, aircraft eligibility, approval queues, and financial totals.
10. Record recovery point, data-loss window, validation evidence, and incident owner.

## Recovery validation

At minimum verify counts and sampled relationships for airlines, users, aircraft, flights, maintenance records, expenses, inventory transactions, documents, audit logs, and migrations. Confirm every sample is tenant-scoped, approved financial records remain unchanged, private attachments require authorization, and aircraft grounding rules still reject assignment.

Recommended objectives must be set by the operating airline. A starting point is daily full backup plus continuous binlogs, RPO no greater than 15 minutes, and documented RTO no greater than four hours; these are planning targets, not guarantees.
