# AMIR — Production Environment Audit
**SKM S005/2026 Demo Sprint · Task D5-22**

Run this audit before every demo deployment and before going live. Each item must be checked and signed off.

---

## Section 1 — Application Configuration

- [ ] `APP_ENV=production` and `APP_DEBUG=false`
- [ ] `APP_KEY` is set (non-empty, 32-byte base64 string via `php artisan key:generate`)
- [ ] `APP_URL` matches the production domain (HTTPS)
- [ ] `BCRYPT_ROUNDS=12` (default; increase to 14 post-launch if acceptable latency)
- [ ] `LOG_LEVEL=error` (not `debug`)
- [ ] `SESSION_ENCRYPT=true`
- [ ] `SESSION_DOMAIN` is set to the production domain

---

## Section 2 — Database

- [ ] `DB_HOST` points to production Postgres 16 instance (ap-southeast-3)
- [ ] `DB_PASSWORD` is set to a strong random value (not default)
- [ ] All migrations applied: `php artisan migrate:status` shows all [Ran]
- [ ] Automated daily backup is configured and tested:
  - Backup job runs at 02:30 MYT (UTC+8)
  - Backup stored in S3 bucket (`AWS_BUCKET`)
  - Test restore completed and documented
- [ ] Point-in-time recovery (PITR) enabled on RDS/managed Postgres

---

## Section 3 — Queue & Horizon

- [ ] `QUEUE_CONNECTION=redis`
- [ ] `HORIZON_SECRET` is set to a non-empty random string
- [ ] `/horizon` dashboard is protected (only accessible with `HORIZON_SECRET` or behind IP whitelist)
- [ ] Horizon supervisor is running: `php artisan horizon:status`
- [ ] At least 2 queue workers configured in `config/horizon.php`
- [ ] `php artisan horizon:terminate` + restart tested — workers resume within 30 seconds

---

## Section 4 — Scheduler

- [ ] Cron entry registered on the server:
  ```
  * * * * * cd /var/www/amir && php artisan schedule:run >> /dev/null 2>&1
  ```
- [ ] Depreciation schedule fires on the 1st of each month at 02:00:
  `php artisan schedule:list` shows `amir:run-depreciation` with `Monthly at 02:00`
- [ ] No duplicate cron entries for the same artisan command

---

## Section 5 — Error Tracking (Sentry)

- [ ] `SENTRY_LARAVEL_DSN` is set to the production DSN
- [ ] `SENTRY_TRACES_SAMPLE_RATE=0.1` (10% of requests traced — adjust after load testing)
- [ ] Test error reaches Sentry:
  ```bash
  php artisan tinker --execute="Sentry\Laravel\Integration::captureUnhandledException(new Exception('AMIR prod test'));"
  ```
  Confirm event appears in Sentry within 60 seconds.
- [ ] Sentry `beforeSend` scrubber active — no PII (NRIC, names, emails) in Sentry events
- [ ] Sentry alert rules configured: notify Slack/email on >5 errors/minute

---

## Section 6 — Storage & S3

- [ ] `FILESYSTEM_DISK=s3` in production
- [ ] `AWS_BUCKET` is set; bucket is private (no public ACLs)
- [ ] `AWS_DEFAULT_REGION=ap-southeast-3` (Malaysia)
- [ ] Versioning enabled on the S3 bucket
- [ ] Lifecycle rule: move objects older than 90 days to Glacier

---

## Section 7 — Mail

- [ ] `MAIL_MAILER` is not `log` in production (use SES, Postmark, or SMTP)
- [ ] `MAIL_FROM_ADDRESS` and `MAIL_FROM_NAME` are set
- [ ] SPF, DKIM, and DMARC records published for the sending domain
- [ ] Send a test password-reset email and confirm delivery

---

## Section 8 — Security Headers

- [ ] HTTPS enforced (HSTS header, min `max-age=31536000`)
- [ ] `X-Frame-Options: SAMEORIGIN`
- [ ] `X-Content-Type-Options: nosniff`
- [ ] `Content-Security-Policy` configured (at minimum: restrict `script-src` to self + Vite CDN)
- [ ] Redis is not publicly accessible (bind to `127.0.0.1` or VPC-internal only)

---

## Section 9 — Multi-Tenancy & Data Isolation

- [ ] `BelongsToTenant` global scope is active on all tenant-scoped models
  - Verify: `php artisan tinker` → `app()->instance('currentTenantId', 'fake-uuid'); App\Domain\Accounting\Models\CoaAccount::count()` → returns 0
- [ ] Cross-tenant isolation smoke test passes (from `docs/SMOKE_TEST.md` Section 15)

---

## Section 10 — Final Checks

- [ ] `php artisan config:cache` has been run
- [ ] `php artisan route:cache` has been run
- [ ] `php artisan view:cache` has been run
- [ ] `npm run build` output is in `public/build/` (Vite manifest present)
- [ ] `php artisan test` passes in CI with zero failures
- [ ] `./vendor/bin/phpstan analyse` passes in CI with zero errors

---

## Sign-off

| Check | Status |
|---|---|
| All 10 sections passed | ☐ |
| Sentry test event confirmed | ☐ |
| Backup restore tested | ☐ |
| Horizon workers running | ☐ |

**Auditor:** \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ &nbsp;&nbsp; **Date:** \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ &nbsp;&nbsp; **Deployment:** \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_

---

*Update this document after each infra change. Ref: AMIR DECISIONS_LOG.md D17 (UTC/MYT), D28 (tenant scoping), E3 (encryption).*
