# Phase 20 — hardening, deployment and recovery

Release **0.20.0** · 2026-09-12 · baseline **0.19.1**.

**Implementation and local verification delivered. Production acceptance remains pending.** This is not a deployed/production-certified release. Phase 19's five management workflows remain complete; none is deferred again.

## Delivered

- Production web, worker and scheduler verify all migration and seed checksums before startup, reject incomplete history/partial rollback, and require no DDL privileges for the check.
- Production MySQL waiting queues are bounded (default 100). Remote Redis requires TLS and authentication; offline buffering is disabled and queued commands capped at 1000.
- Database readiness now times out while waiting for a connection. Worker/scheduler polling removes abort listeners after every interval, preventing accumulation over long runs.
- `bin/backup.js` provides streaming gzip/AES-256-GCM database/file encryption, private key/credential file checks, authenticated restore output, decompression limits and no-overwrite behavior. The MySQL wrapper publishes only after successful client completion and preserves fast client output while encryption initializes.
- Docker runtime/build/acceptance profiles, host Nginx, three systemd units, PM2 configuration and cPanel compatibility guidance are included. Production secrets remain outside source/images. CI has local quality/audit and isolated MySQL/Redis jobs; it has not been installed or run in an external GitHub repository.
- Isolated live-service acceptance runner covers migration/seed replay, competing ledger operations, immutable-entry triggers, outbox row claims and Redis atomic limits. A bounded loopback liveness load runner and production preflight command are supplied.
- Deployment, backup/restore, security and host/device acceptance guides identify exact remaining checks and recovery risks.

## Verification

**Local quality gate: PASS — 437/437 tests, zero failures or skips.** Lint, formatting, environment/schema verification, both CSS builds and project structure passed. There are 23 new Phase 20 regression tests. Both production and full dependency-tree audits reported zero vulnerabilities. The 100-request local liveness profile returned HTTP 200 for every request (concurrency 5, p95 6 ms); these timings are fixture-specific.

Final executed counts and outputs are recorded in `PHASE-20-QUALITY-GATE.json` and `PHASE-20-QUALITY-CHECK.txt`. The production dependency audit output is preserved as `PHASE-20-DEPENDENCY-AUDIT.json`. The local loopback liveness profile is in `PHASE-20-LOAD-CHECK.json`.

The new tests exercise schema drift/pending history, production Redis/queue policy, test-database guardrails, byte-exact encrypted backup recovery, malformed/tampered/truncated backups, wrong keys, size limits, no-overwrite behavior, failed/successful dump CLI handling, listener cleanup and readiness during a stalled database queue. CLI dump tests use a disposable fake client; they do not prove real MySQL recoverability.

The loopback liveness profile runs 100 requests at concurrency 5 against the real Express shell with fixture readiness and a memory rate-limit store. It does not measure authenticated business traffic, MySQL/Redis capacity or production latency.

## Remaining acceptance

| Check                                              | Why still open                                                                                   |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Live MySQL/Redis migrations and contention         | No local service binaries or supplied target services; platform package setup could not complete |
| Docker/Nginx/systemd runtime and image/OS scan     | Tools/target host not available here; templates reviewed, YAML parsed, not natively deployed     |
| Native browser/device/Telegram UI                  | Cloud browser could not open the local preview (`ERR_BLOCKED_BY_CLIENT`); no visual PASS claimed |
| Real database/media backup restore                 | Requires a disposable recovery host, actual database snapshot, keys and operator evidence        |
| Production load, faults, webhook/provider behavior | Requires target infrastructure, chosen domain/bot and controlled end-to-end acceptance           |

There was no public deployment, bot webhook change, invitation delivery, broadcast, provider payment or real Telegram message. Free-only reward rules remain unchanged.

## Upgrade

No new migration, dependency or application environment key is added. Existing 0.19.1 installations keep migrations 001–023 and six seeds unchanged. Set a positive `DATABASE_QUEUE_LIMIT` if an older environment has 0. Remote Redis must use authenticated TLS. Complete seeds before production startup, and ensure the process manager's stop budget exceeds the configured application shutdown timeout.

Use `deployment.md` for the ordered procedure and `backup-restore.md` before changing a live installation. Keep the previous release and verified backup recoverable. The complete Phase 20 source ZIP is accompanied by a SHA-256 file and a byte-for-byte package verification report.
