# WEBX Travel — Release Candidate Report

_Phase 12 — hardening, audit, backups, migrations & delivery._

This is the release report and delivery notes for WEBX Travel, a multi-tenant SaaS
for travel agencies (one central database + one database per tenant). It is
written to the acceptance bar of Phase 12: **do not declare "production ready" if
a high/critical failure persists, restore was not rehearsed, or multi-tenant
isolation was not verified.**

---

## 1. Tested stack (exact versions)

| Component | Version | Notes |
|---|---|---|
| PHP | **8.3.33** (dev/test) → **8.4** (production target) | Portable PHP 8.3 in `_devtools/` is used for local build+test; the cPanel host runs PHP 8.4. |
| Laravel | **12.68.0** | `laravel/framework ^12.0` |
| Sanctum | **4.x** | Tenant API tokens (per-tenant DB) |
| PHPUnit | **11.5.50** | via `nunomaduro/collision ^8.6` |
| Vue | **3.5.41** | SPA admin + Operación PWA |
| Vite | **7.x** | Production bundle |
| TypeScript | **5.6** | pinned (vue-tsc 3 breaks on TS 7) |
| Tailwind CSS | **4.x** | `@theme` tokens |

> **PHP EOL note.** The original prompt package targeted PHP 8.1 / Laravel 10.
> During Phase 01 that stack was rejected (Composer refuses to install Laravel 10
> due to unpatched advisories, and the host runs PHP 8.4). WEBX Travel is built on
> **Laravel 12 + PHP 8.3/8.4**, which **already supersedes** the EOL risk the phase
> asks to mitigate — there is no PHP 8.1/Laravel 10 to migrate away from. The
> roadmap item is therefore "stay current on Laravel 12.x LTS patch releases",
> not a major-version rescue.

## 2. Module coverage & acceptance

| Phase | Module | Key guarantees | Status |
|---|---|---|---|
| 01–02 | Tenancy/Platform foundation | central+tenant split, domain resolution, encrypted connections, guard (domain/tenant/subscription) | ✅ |
| 03 | CRM | buyer ≠ traveler, reusable passengers, encrypted+masked documents, signed downloads, auditable merge | ✅ |
| 04 | Sales | 4 independent states, derived ledger balance (bcmath, never float), immutable idempotent payments, reversals/refunds, plans, cancellation | ✅ |
| 05 | Tours & inventory | derived capacity = min limiting resource, no oversell (lockForUpdate), idempotent holds, transfers keep history | ✅ |
| 06 | Lodging & rooming | sale ≠ physical assignment, derived availability, atomic assign/move/swap + history, idempotent holds | ✅ |
| 07 | Suppliers/costs/payables | establishment ≠ supplier ≠ committed cost ≠ payment, immutable idempotent supplier payments, derived balances, encrypted+audited CLABE, separated duties | ✅ |
| 08 | Dashboards & reports | single source of truth, per-currency (never summed), brand-scoped base query, profit flagged estimated, reproducible exports (CSV formula-neutralized), permission-gated columns | ✅ |
| 09 | QR / check-in / PWA | random hashed passes per passenger+tour, immutable idempotent check-in, undo=compensating event, financial informs not blocks, coordinator scope, installable PWA | ✅ |
| 10 | Portal / documents / comms | magic-link scope, snapshot documents on private disk, queued idempotent communications, no supplier costs in client docs | ✅ |
| 11 | Super Admin + billing | resumable idempotent provisioning, entitlements (backend+frontend), TOTP 2FA, audited impersonation, monthly/annual subscriptions + invoices + dunning + gateway adapter | ✅ |
| 12 | Hardening/audit/backup/migrations | append-only audit, batch migration runner (failure-isolated), encrypted backups + rehearsal restore, security headers, isolation | ✅ |

## 3. Commands executed & results

```bash
# Backend suite (per-tenant provisioning; SQLite in tests)
php artisan test            # 148 passing (see final run in CI/log)

# Frontend
npm run type-check          # vue-tsc --noEmit — clean
npm run build               # vite build — succeeds

# Formatting / static
vendor/bin/pint             # clean

# Multi-tenant migrations
php artisan tenant:migrate-all --dry-run   # inventory, applies nothing
php artisan tenant:migrate-all             # batch, per-tenant failure isolation
php artisan core:migrate                   # central schema
```

## 4. Evidence — isolation & restoration

- **Multi-tenant isolation.** `TenantIsolationTest`, `TenantJobIsolationTest`,
  `CredentialSecurityTest`, `MigrationSeparationTest`, and
  `Hardening\SecurityAndIsolationTest` prove: each tenant has its own database;
  a job for tenant A never uses tenant B's connection; core tables live only in
  central; two tenants never see each other's data; a QR token / portal magic
  link from one tenant never resolves in another. Reporting metrics are proven to
  not cross tenants (`Reporting\ConsolidatedBrandsTest`).
- **Backup & rehearsal restore.** `Hardening\BackupRestoreTest` backs up a tenant
  (encrypted at rest, sha256 checksum, inventoried in `tenant_backups`), then
  **restores to an isolated target** and validates checksum + row counts (roles,
  users). The existence of a file is never treated as proof of recovery.
- **Failed migration isolation.** `Hardening\MigrationRunnerTest`: with tenant B
  broken, the batch run marks **B failed** (not updated), still migrates **A**,
  and reports both — a failure on B never blocks or misreports A/C.
- **Financial integrity.** Payments/supplier-payments/receipts are immutable;
  corrections are reversals/compensating events (Phases 04/07/09). The unified
  `audit_logs` trail is **append-only** (update/delete throw) with secret
  redaction (`Hardening\AuditTest`).

## 5. Open risks (by severity)

| Sev | Risk | Mitigation / status |
|---|---|---|
| **Medium** | Real payment collection (card charging) is **not** connected. | By design + policy: a `PaymentGateway` adapter interface + idempotent webhook inbox are shipped; a processor (Stripe/Mercado Pago) is connected later with credentials outside the codebase. Manual payment recording is fully functional now. |
| **Medium** | SSL issuance / DNS verification automation depends on the host, which has no API. | Modeled as domain states + steps; operation is a documented manual procedure. Unverified domains never route as active. |
| **Low** | CSP uses `'unsafe-inline'` for scripts/styles (blade shells + fonts). | Compatible and safe against external injection; tighten with per-request nonces (roadmap). |
| **Low** | Offline check-in (deliberate download, IndexedDB queue, conflict UI) deferred. | Online PWA complete; schema already carries `mode`/`device_id`/`device_at`/`idempotency_key`. |
| **Low** | Queue/scheduler on shared cPanel (no Redis/Horizon). | Database queue + cron; documented. Redis/workers on the roadmap. |
| **Info** | Full audit wiring is demonstrated on `payment_received`; other events have their own trails (BookingEvent, RoomAssignmentEvent, CheckinEvent, PlatformAuditLog). | Route remaining critical events through `AuditService` incrementally — mechanism is in place. |

_No high/critical open failures._

## 6. Deployment steps (cPanel shared hosting, PHP 8.4)

1. **Build locally** (host has no SSH/Composer/modern Node): `composer install --no-dev -o`,
   `npm ci && npm run build`.
2. Upload the project via File Manager; keep `storage/` and `bootstrap/cache/`
   writable; point the domain docroot at `public/`.
3. Create the **central** MySQL DB in cPanel; set `.env` (`APP_KEY`, DB creds,
   `MAIL_*`, `QUEUE_CONNECTION=database`, tenant DB host/creds). Never commit `.env`.
4. `php artisan core:migrate` (central schema) then seed platform data (plans,
   features, a super-admin with 2FA).
5. Provision the first tenant via the Super Admin (`/superadmin`) or
   `php artisan tenant:provision`; it runs the resumable workflow and only marks
   the tenant Active after the smoke test.
6. **Cron** (cPanel): `* * * * * php /path/artisan schedule:run` (scheduler with
   overlap protection); a queue worker via `php artisan queue:work --stop-when-empty`
   on a frequent cron if no daemon is available.
7. **Backups** (cron or manual): dump central + each tenant DB + private
   `storage/app/tenants` files; encrypt + checksum + inventory; run a **rehearsal
   restore** on a schedule. RPO target ≤ 24h, RTO target ≤ 4h (adjust per plan).

### Runbooks (summary)
- **Migrate:** `tenant:migrate-all --dry-run` → review → `tenant:migrate-all`. A
  failed tenant is reported and skipped; never `migrate:fresh` in production.
- **Rollback:** per-migration `down()` on the affected tenant only; restore from
  the latest verified backup if data is affected.
- **Incident (one tenant degraded):** suspend the tenant (data kept), investigate
  with impersonation (audited), fix, reactivate. Maintenance mode for platform-wide
  incidents.

## 7. Recommendation

**Release-candidate — conditionally production ready.** Multi-tenant isolation is
verified, a backup restore was rehearsed and validated, and there are no
high/critical open failures. Before onboarding paying agencies:
1. Connect the real payment gateway (adapter + webhook are ready) **or** operate on
   manual invoicing;
2. Configure production `.env`, cron, queue and the backup+rehearsal schedule on
   the host;
3. Seed a super-admin with 2FA and verify the first custom domain end-to-end.

The stack is already on Laravel 12 / PHP 8.4, so **no PHP 8.1 → newer migration is
required**; keep current on Laravel 12.x patch releases and revisit Redis/workers +
object storage as tenant volume grows.
