OViq Hotel ERP — Phase 6 hardened development build THIS IS NOT A PRODUCTION CERTIFICATION OR THE COMPLETE ORIGINAL MODULE MATRIX. Read RELEASE-STATUS.txt for feature limitations and acceptance gates. 1. REQUIREMENTS PHP >=8.2, PDO MySQL, mbstring, OpenSSL and Argon2id support. MySQL 8.0+, transactional InnoDB, strict SQL mode. PHP-FPM and HTTPS for production. The executed runtime was PHP 8.3.6 / MySQL 8.0.46 / Chromium. PHP 8.2 itself, Safari, Firefox and Windows hosting were not exercised. Bootstrap 5.3.3 is vendored locally, with its MIT license included. No Composer install or external CDN is needed for the shipped UI. 2. FRESH INSTALL Use a new EMPTY database. Set the web document root ONLY to public/. Restrict /installer/ to your operator IP or HTTP authentication during setup. Allow the PHP process to write storage/ and TEMPORARILY config/. Open /installer/ and complete the wizard (admin password >=12 characters). After success, remove config write permission and deny /installer/ at the server. Keep storage/installed.lock. Do not expose app/, core/, tests/, config/ or bin/. Do not use PHP's development server for live operations. 3. UPGRADE FROM PHASES 1–5 Back up the full database, config/, storage/uploads/ and installed.lock first. Review deployment/preflight.sql for legacy-data inconsistencies. Put the property application in maintenance mode and stop background writers. Deploy code WITHOUT overwriting local config files, uploads or installed.lock. From project root run: php bin/migrate.php MySQL DDL auto-commits. The metadata-aware runner supports retry; reversal needs restoring the pre-upgrade database AND matching code/config release together. For old globally shared inventory items, automatic ownership is assigned only when existing stock belongs to exactly one property. Remaining NULL owners are quarantined from normal UI and require explicit operator reconciliation. Do not assign ambiguous or shared inventory to an arbitrary property. Historical room charges have no reliable business date in older builds. Reconcile their business_date manually from verified source records; never infer it from posted_at alone and never delete financial history to pass a preflight. Reconcile any pre-upgrade POS group/receipt or inventory valuation differences. 4. DBA AUDIT GUARDS AND RUNTIME PRIVILEGES — REQUIRED BEFORE LIVE USE Run as the DBA (separate from the web runtime): php bin/install-audit-guards.php Optional setup connection overrides: OVIQ_SETUP_HOST, OVIQ_SETUP_USER, OVIQ_SETUP_PASS. Use secret management; do not commit actual credentials. Binary-logged MySQL may require DBA privileges to create triggers. Do not grant SUPER to the runtime account or weaken server controls just to finish setup. The two triggers reject UPDATE/DELETE on audit_logs. They do not defend against a privileged operator dropping/truncating tables or disabling protections. Use a separate runtime identity with table-specific DML grants and only SELECT/INSERT on audit_logs. See deployment/runtime-grants.sql.example. Use external append-only audit replication for privileged-operator resilience. Then run: php bin/preflight.php Any BLOCK output is a deployment stopper, not an informational warning. A passing preflight checks only its listed invariants, not general security. 5. BACKUP AND RESTORE Install the MySQL client tools. Use an independent backup database identity. Run: php bin/backup.php /secure-backups/oviq-before-upgrade.sql Optional overrides: OVIQ_BACKUP_HOST, OVIQ_BACKUP_USER, OVIQ_BACKUP_PASS. The utility refuses overwrites, uses no shell string, creates owner-only files, keeps passwords out of process arguments, removes incomplete files and prints a SHA-256 digest. It does not encrypt backups: encrypt and restrict them outside public/, apply a retention policy and protect guest/passport/payment metadata. For a restore drill, have the DBA create a NEW disposable database and load the SQL there with the MySQL client. Match table/record counts, journal balances, audit triggers and application acceptance tests before a production restore. The supplied release verification includes an isolated actual restore drill. Do not restore into an occupied live database without a maintenance plan. 6. SERVER CONFIGURATION Adapt deployment/nginx.conf.example and deployment/php.ini.example. Test syntax before reloading. Configure real HTTPS, secure cookies, log rotation, least-privilege file ownership and monitoring. No web-server configuration in this package has been deployed or certified on your infrastructure. The UI still has inline JavaScript; a strict nonce-based CSP migration is pending. Mutation endpoints accept HTML form-encoded/multipart payloads only, not JSON. CSRF tokens are checked centrally and in action handlers. Authenticated HTML/API content is network-only. Offline fallback is static; no offline guest records, queued writes or automatic resubmission are supported. 7. TESTS The *_test suffix safety check does NOT guarantee a database is disposable. Use a fresh dedicated database/user with no access to real hotel data. Load installer/schema.sql, installer/seed.sql, then install DBA audit guards. Set OVIQ_TEST_DB, OVIQ_TEST_HOST, OVIQ_TEST_USER, OVIQ_TEST_PASS. Run: php tests/integration.php Run: php tests/hardening.php The latter starts independent PHP worker processes for race tests and requires proc_open, filesystem access to a temporary directory and MySQL connections. See tests/verification.txt for exact executed results and coverage limitations. 8. CUTOVER CHECKLIST Complete all BLOCK gates; confirm tested backup and rollback; rotate setup credentials; disable installer; verify HTTPS/session cookies; obtain legal/tax and accounting sign-off; train staff; run a staged pilot with test payments; complete external integration sandbox testing separately; obtain explicit business acceptance. Concurrency control uses a coarse property-row mutex: correctness was race-tested, but high-load throughput remains unbenchmarked.