# v77 — Production Readiness

v77 is the production-hardening sprint after the v76 end-to-end POS checkpoint.

## What v77 proves

v77 does not claim that a machine is production-ready merely because application tests
pass. It creates explicit, repeatable gates for the target environment.

### Fail-fast application gate

`php artisan ops:production-check`

Critical checks:
- `APP_ENV=production`
- debug disabled
- non-placeholder `APP_KEY`
- HTTPS `APP_URL`
- PostgreSQL business database
- Redis cache, queue, and sessions
- encrypted Redis sessions and secure cookies
- complete PostgreSQL schema `search_path`
- live database/cache/queue connectivity
- writable Laravel storage

Warnings (such as logging configuration) do not mask critical failures.

For build/CI inspection without touching external dependencies:
`php artisan ops:production-check --no-runtime`

### Security response baseline

All Laravel responses receive:
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options`
- `Referrer-Policy`
- `Permissions-Policy`
- HSTS only for secure production requests

### Release identity

`GET /up/version` exposes only:
service name, release version, Git commit, and build timestamp.

`/up/ready` also includes non-secret release identity. Deployment can therefore prove
which build is actually serving traffic.

### Deployment workflow

`ops/deploy/deploy-api.ps1`:
1. maintenance mode
2. clear stale optimized caches
3. production Composer install
4. static production gate
5. forced migrations
6. Laravel optimize
7. runtime production gate
8. queue restart
9. leave maintenance mode in `finally`

The script does not create or overwrite production secrets.

### Web release package

`ops/deploy/package-web.ps1` runs clean install, lint, tests, build, then creates a ZIP
and SHA-256 checksum.

### Production smoke

`ops/deploy/smoke-production.ps1` verifies:
- liveness
- readiness
- release endpoint
- optional expected commit identity

### PostgreSQL disaster recovery

Backup now:
- creates custom-format dump
- validates it with `pg_restore --list`
- creates SHA-256
- applies configurable retention

Restore now verifies the sidecar SHA-256 before changing the target DB when present.
Database-name confirmation remains mandatory.

A successful backup is not a restore drill. Production sign-off still requires restoring
a recent dump into a disposable database and verifying application migrations/readiness.

### Windows Local Agent

Production tooling can:
- publish a `win-x64` Agent package with checksum
- install/update the Agent as an automatic Windows Service
- configure Windows service restart recovery

Pairing credentials remain DPAPI protected per v61.

### CI

CI additionally:
- validates Composer manifest
- proves `ops:production-check` registration
- publishes the Windows Agent for `win-x64`

## Production sign-off boundary

Application-level readiness is complete only after the target infrastructure passes:
- secrets injected outside Git
- TLS termination confirmed
- PostgreSQL/Redis durable production services
- queue worker supervised
- scheduler invoked every minute
- backup retention configured
- restore drill completed
- `/up`, `/up/ready`, `/up/version` checked from outside the host
- smoke checkout and printing acceptance completed
- rollback owner and deployment window confirmed
