# Tarnova Backup Restore Drill

A backup is not trusted until it has been restored successfully in a safe environment. Run this drill before go-live, after major schema changes, and at least every 90 days.

## Goal

Prove that Tarnova can recover:

- the database
- uploaded logos and public storage assets
- private proof-of-payment files
- generated/public document access paths
- queue/session/cache tables needed by the app

Do not run a restore test directly over production unless you are performing an approved disaster recovery event.

## 1. Prepare a Safe Restore Environment

Use a separate local, staging, or temporary server environment.

Required:

- same PHP major/minor version as production
- same database engine family as production
- same app commit or release artifact
- isolated database name and credentials
- no real outgoing mail unless intentionally testing mail
- `APP_ENV=staging` or `local`
- `APP_DEBUG=false` if testing production-like behavior

Recommended safety settings:

```bash
MAIL_MAILER=log
QUEUE_CONNECTION=sync
APP_URL=https://restore-test.example.com
```

## 2. Create or Select a Backup

On production, confirm a recent backup exists:

```bash
php artisan backup:list
php artisan backup:monitor
```

If you are creating a fresh drill backup:

```bash
php artisan backup:run
php artisan backup:list
```

Copy the selected backup archive to the restore environment. Keep the archive password available from secure secret storage, not from the web root.

## 3. Restore Application Files and Uploads

Extract the backup archive into a temporary directory.

Confirm the restored archive contains:

- application source files
- `storage/app/public` uploads where applicable
- `storage/app/payment-proofs` or private proof files if present
- database dump file

Install dependencies in the restore environment:

```bash
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan storage:link
```

Ensure these directories are writable by PHP:

```text
storage/app
storage/logs
storage/framework
bootstrap/cache
```

## 4. Restore the Database

Create a fresh empty restore database first. Never import into production during a drill.

Import the SQL dump using the database tool matching your production engine, for example MySQL:

```bash
mysql -u restore_user -p restore_database < database.sql
```

Then run safe Laravel checks:

```bash
php artisan migrate:status
php artisan app:audit-data-integrity
php artisan payments:audit-revenue
```

Do not run destructive migrations against restored production data unless this is part of a controlled deployment rehearsal.

## 5. Verify Business-Critical Screens

Log in to the restore environment and check:

- dashboard loads
- invoice list loads
- invoice detail loads
- invoice PDF downloads
- quotation detail and PDF/load path work
- client statement loads
- credit notes load
- payments and collections load
- uploaded company logo displays on client documents
- proof-of-payment files are accessible only through authorized routes
- public invoice/quotation/statement links work if the restored `APP_URL` is configured for the test environment

## 6. Verify Money Reconciliation

Run:

```bash
php artisan app:audit-data-integrity
php artisan payments:audit-revenue
php artisan test tests\Feature\DataIntegrityAuditCommandTest.php
php artisan test tests\Feature\MoneyCalculationRulesTest.php
```

The restore is not acceptable if invoices, payments, credit notes, collections, dashboard totals, or statements do not reconcile.

## 7. Record the Drill

After the restore is proven, set this on production during the next deploy/config update:

```bash
BACKUP_RESTORE_TESTED_AT=YYYY-MM-DD
```

Then run:

```bash
php artisan config:cache
php artisan app:deployment-readiness
```

## 8. Failure Rules

If the restore fails:

- do not trust the backup strategy
- do not go live with real customer financial data
- fix the missing database/files/secrets/permissions issue
- run the drill again until it passes

A failed restore drill is a production blocker, not a nice-to-have bug.