# Tarnova Production Readiness Checklist

This is the minimum go-live checklist for running Tarnova safely with real business data. Do not treat this as optional polish: mail, queues, scheduler, backups, SSL, storage, and monitoring are operational requirements.

## 1. Environment Safety

Set production values in `.env` on the server. Never commit real secrets.

Required values:

- `APP_NAME=Tarnova`
- `APP_ENV=production`
- `APP_KEY=base64:...`
- `APP_DEBUG=false`
- `APP_URL=https://your-production-domain`
- `LOG_CHANNEL=stack`
- `LOG_STACK=daily`
- `LOG_LEVEL=warning`

Database:

- `DB_CONNECTION=mysql`
- `DB_HOST=...`
- `DB_PORT=3306`
- `DB_DATABASE=...`
- `DB_USERNAME=...`
- `DB_PASSWORD=...`

Sessions, cache, and queue:

- `SESSION_DRIVER=database`
- `SESSION_ENCRYPT=true`
- `SESSION_SECURE_COOKIE=true`
- `SESSION_HTTP_ONLY=true`
- `SESSION_SAME_SITE=lax`
- `CACHE_STORE=database` or a production cache driver
- `QUEUE_CONNECTION=database` or a worker-backed production queue driver
- `QUEUE_FAILED_DRIVER=database-uuids`

## 2. Real Mail Driver

Do not use `log` or `array` mailers in production.

Use a real provider such as SMTP, SES, Postmark, Resend, Mailgun, or your host's transactional mail service.

DNS requirements for the sending domain:

- SPF includes the selected mail provider.
- DKIM is enabled and verified in the provider dashboard.
- DMARC exists for the domain, starting with a monitored policy such as `p=none` and tightening later when delivery is proven.
- `MAIL_FROM_ADDRESS` uses a verified sender/domain. Avoid free mailbox sender domains for business mail.

Required mail values:

- `MAIL_MAILER=smtp` or another real provider
- `MAIL_HOST=...`
- `MAIL_PORT=465` or provider-specific port
- `MAIL_USERNAME=...`
- `MAIL_PASSWORD=...`
- `MAIL_ENCRYPTION=ssl` or provider-specific encryption
- `MAIL_EHLO_DOMAIN=your-domain.example` when required by your provider
- `MAIL_FROM_ADDRESS=verified-sender@your-domain`
- `MAIL_FROM_NAME="Tarnova"`
- `MAIL_QUEUE_WHEN_AVAILABLE=true` only when the queue worker is supervised and running

Current behavior note: invoice, quotation, and credit note emails use dedicated queued workflow jobs when queueing is enabled. Statement, reminder, and password reset emails send synchronously so their user-facing result and sent logs reflect the completed mail operation.

Smoke test after deploy:

- Send a password reset email.
- Send an invoice email to an internal/test client.
- Send a quotation email to an internal/test client.
- Send a client statement email.
- Send a manual reminder from an eligible invoice.
- Confirm emails arrive in inbox/spam and no errors appear in logs.
- Confirm your provider dashboard shows accepted delivery and no bounce/suppression events.

## 3. Queue Worker

Tarnova queues mail when `MAIL_QUEUE_WHEN_AVAILABLE=true` and `QUEUE_CONNECTION` is not `sync`.

Run a supervised queue worker:

```bash
php artisan queue:work --sleep=3 --tries=3 --timeout=120
```

Use Supervisor, systemd, Laravel Forge, Ploi, cPanel daemon tooling, or your host's process manager.

After every deployment:

```bash
php artisan queue:restart
```

Operational checks:

```bash
php artisan queue:failed
php artisan queue:retry all
```

If queue workers are not running, queued invoice, quotation, and credit note emails may sit in the `jobs` table and never send. Statement, reminder, and password reset emails still require the same provider configuration but are not queued by the document workflow jobs.

## 4. Scheduler

Add exactly one production cron entry:

```cron
* * * * * cd /path/to/invoice-app && php artisan schedule:run >> /dev/null 2>&1
```

Scheduled commands currently include:

- `invoices:generate-recurring`
- `invoices:refresh-overdue`
- `quotations:expire`
- `invoices:send-reminders`

Verify:

```bash
php artisan schedule:list
```

The scheduler must run from the server. Do not depend on browser visits to trigger reminders or recurring invoices.

## 5. Storage Link and Files

Run:

```bash
php artisan storage:link
```

Confirm:

- `public/storage` exists.
- Uploaded company/client document logos load correctly.
- Proof-of-payment files remain private unless served through authorized routes.
- `storage/app`, `storage/logs`, and `bootstrap/cache` are writable by the web/PHP user.

Include uploaded/public storage in backups.

## 6. SSL and Web Server

Production must use HTTPS.

Required:

- Valid SSL certificate.
- HTTP redirects to HTTPS.
- `APP_URL=https://...`
- `SESSION_ENCRYPT=true`
- `SESSION_SECURE_COOKIE=true`
- Web root points to `/public`, not the project root.
- `.env`, `storage`, `vendor`, and source files are not web-accessible directly.

## 7. Deployment Commands

Run from the release directory:

```bash
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan migrate --force
php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart
```

If anything fails, stop the deployment and fix it before sending real documents.

## 8. Preflight Commands

Before opening the app to real customers, run:

```bash
php artisan app:deployment-readiness
php artisan app:audit-data-integrity
php artisan test --filter=ProductionReadinessTest
php artisan test --filter=DataIntegrityAuditCommandTest
```

Use strict readiness mode when testing the actual production server:

```bash
php artisan app:deployment-readiness --strict
```

## 9. Backups

Tarnova uses `spatie/laravel-backup` for scheduled production backups.

Required production values:

- `BACKUP_NAME="${APP_NAME}"`
- `BACKUP_DISKS=local` for local server storage, or `s3`/remote disk after configuring `config/filesystems.php`
- `BACKUP_ARCHIVE_PASSWORD=...` required before real financial data is backed up; use a long random secret and store it outside the web root
- `BACKUP_VERIFY=true`
- `BACKUP_NOTIFICATION_MAIL_TO=admin@your-domain`
- `BACKUP_MAX_AGE_DAYS=1`
- `BACKUP_MAX_STORAGE_MB=5000`
- `BACKUP_DELETE_OLDEST_WHEN_USING_MORE_MB=5000`

Scheduled commands registered in `routes/console.php`:

- `backup:run` daily at 01:10
- `backup:clean` daily at 01:40
- `backup:monitor` daily at 06:30

Manual commands:

```bash
php artisan backup:run
php artisan backup:list
php artisan backup:monitor
php artisan backup:clean
```

Backups include the application and storage data, while excluding `vendor`, `node_modules`, `.env`, framework cache, and logs. Keep a separate secure copy of production `.env` outside the web root.

Minimum backup plan:

- Database and uploaded files backed up at least daily.
- Uploaded logos and proof-of-payment files are protected.
- Production `.env` is backed up separately and securely.
- Backups are not stored under `/public`.
- Restore is tested before go-live.

A backup that has never been restored is only a hope with a filename. Test it.

## 10. Monitoring

At minimum, configure:

- Uptime monitoring for `/up`.
- Error/log alerting for production exceptions.
- Failed queue job monitoring.
- Disk usage alerts for `storage` and database backups.
- Scheduler/cron failure alerts if your host supports them.
- Mail provider bounce/failure monitoring.

Manual checks after deployment:

- `/up` returns healthy.
- Login works.
- MFA challenge works.
- Dashboard loads.
- Invoice PDF downloads.
- Public invoice link works.
- Pay Now / EFT proof page loads.
- Queue worker is running.
- Scheduler is registered.
- No new critical errors in `storage/logs`.

## 11. Rollback Notes

Before deploy, keep:

- Previous release artifact/directory.
- Database backup from immediately before migrations.
- Previous `.env` and web-server config.

If rollback is needed:

```bash
php artisan down
# restore previous release files
# restore database backup if migrations changed incompatible schema/data
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan up
```

## 12. Hard Rules

- Never run production with `APP_DEBUG=true`.
- Never deploy with `MAIL_MAILER=log` or `array` for real customer emails.
- Never enable queued mail without a running queue worker.
- Never skip the scheduler if using reminders or recurring invoices.
- Never rely on local browser testing as production monitoring.
- Never store backups under the public web root.
