# 02 · Staging Deployment

Staging exists to answer one question honestly: *would this break production?* It is only worth
having if it is close enough to production that its answer means something — and different enough
that nobody confuses the two.

Follow [03-PRODUCTION-DEPLOYMENT.md](03-PRODUCTION-DEPLOYMENT.md) for the server build. This
document covers only what should differ, and why.

---

## 1. What staging is for

- **Rehearsing deployments and migrations** against realistic data volumes before touching live
- **Training staff** without a mistake becoming a real customer's problem
- **Demonstrating** the system to stakeholders
- **Reproducing bugs** reported from production, safely

If a change has not run on staging, you do not know that its migration works.

---

## 2. What must differ from production

| Setting | Staging | Why |
|---|---|---|
| `APP_ENV` | `staging` | |
| `APP_DEBUG` | `false` | Staging should fail the way production fails. Turn it on temporarily to chase a specific bug, then turn it back off. |
| `APP_URL` | `https://staging.rusukh.pk` | |
| `PAYFAST_MODE` | `fake` | **Never `live`.** Real money must not move from a staging server. |
| `MAIL_MAILER` | `log`, or SMTP to a catch-all | **The single most important line here** — see below |
| `TELESCOPE_ENABLED` | `true` | Genuinely useful here; must stay off in production |
| `LOG_LEVEL` | `debug` | Disk is cheap on staging |
| Database | `rusukh_staging`, separate server or instance | Must not share a server with production data |
| Redis | Separate instance, or a different `REDIS_DB` index | A shared Redis will collide sessions and queues between environments |

### Email is the trap

Staging usually runs with a copy of production data. That copy contains **real customer email
addresses**. Point staging at a real SMTP server and your test order will email an actual customer
that their real suit is ready.

Pick one, and verify it before the first seed:

```ini
# Safest: emails are written to storage/logs/laravel.log and never sent.
MAIL_MAILER=log
```

```ini
# Better for testing the actual templates: a catch-all inbox that accepts
# everything and delivers nothing onward (Mailpit, Mailtrap, Mailhog).
MAIL_MAILER=smtp
MAIL_HOST=mailpit.internal
MAIL_PORT=1025
```

---

## 3. Lock it out of the public internet

Staging usually holds a copy of real customer data, so treat it as sensitive even though it is
"only staging".

```nginx
# HTTP basic auth over the whole site
auth_basic           "Rusukh Staging";
auth_basic_user_file /etc/nginx/.htpasswd-staging;

# Or IP allow-listing, if your team has fixed addresses
# allow 203.0.113.0/24;
# deny  all;
```

Also add a `noindex` header so a leaked link never reaches a search engine:

```nginx
add_header X-Robots-Tag "noindex, nofollow" always;
```

---

## 4. Getting data into it

### Option A — the demo dataset (no real customer data)

Best for training and demos. Nothing in it is real, so nothing can leak.

```bash
php artisan migrate:fresh --seed --force
```

Roughly 120 orders across every status including failure paths, a full staff roster, stock,
production jobs, payments, ledger entries and matured commissions. Credentials are in
[`../RUNBOOK.md`](../RUNBOOK.md) §1.

### Option B — a production copy (realistic, and now sensitive)

Best for rehearsing a migration against real data volumes. **The moment you restore this, staging
holds real personal data** — real names, addresses, phone numbers and encrypted CNICs.

```bash
pg_dump -Fc -h prod-db rusukh > /tmp/prod.dump
pg_restore -d rusukh_staging -c /tmp/prod.dump
php artisan migrate --force
```

Then immediately, in the same session:

```bash
# Confirm mail cannot escape
php artisan tinker --execute="echo config('mail.default');"     # expect: log

# Confirm payments are simulated
php artisan tinker --execute="echo config('rusukh.payments.payfast.mode');"  # expect: fake
```

Consider scrubbing personal data on restore (replace customer emails with
`customer+<id>@example.invalid`, blank phone numbers) unless you specifically need real values.

---

## 5. Rehearsing a deployment

The point of staging is that this sequence is boring by the time you run it on production:

```bash
# 1. Snapshot, so you can rerun the rehearsal
pg_dump -Fc rusukh_staging > /tmp/pre-deploy.dump

# 2. Deploy exactly as production will
git pull
composer install --no-dev --optimize-autoloader
npm ci && npm run build
php artisan migrate --force          # time this — a slow migration means downtime
php artisan optimize:clear && php artisan config:cache && php artisan route:cache
php artisan queue:restart

# 3. Verify
php artisan test --group=smoke
```

Then walk the critical paths by hand: create an order, take a payment, run a garment through
cutting and QC, dispatch it, and confirm the emails appear in the log or catch-all inbox.

> **The first sign-in will look like a failure, and is not.** `super_admin`, `management` and
> `finance` hold root or financial power, so `EnsureTwoFactor` guards **every** `/admin` request they
> make — not only the first. Signing in as `root@rusukh.pk` on a fresh box therefore lands on
> `/admin/identity/security/two-factor` and keeps landing there until an authenticator app is
> enrolled. That is the control working. Enrol once and the session proceeds normally; the other
> eleven roles are unaffected and can be used to walk the paths above immediately.

If the migration took 40 seconds on staging, it will take longer on production. Plan the
maintenance window from the number you measured, not the one you hoped for.

---

## 6. Keeping staging honest

Staging rots. When it does, it stops being evidence and starts being noise.

- **Refresh it on a schedule** — monthly, or before any significant release
- **Reset after training sessions**, so the next person starts from a known state
- **Never let staging drift ahead of production** — the same branch, the same migrations
- **Fix broken staging immediately.** A staging server that has been red for a week teaches
  everyone to ignore it, and then it is worse than not having one
