Deployment
For the isolated MySQL v14 target, mandatory backup/DNS gates and observed Azure state, use the v14 runbook. Existing v13 commands below remain compatibility interfaces; they do not authorize modifying the old target.
Canonical production URL: https://asi-database-node-api.arybit.co.ke
First staging installation
Install Python 3.12+, Bash, Git, Azure CLI, SSH/SCP and the required HTTPS tools. Authenticate to the intended Azure tenant/subscription. Pin the VM SSH host key through a trusted channel. Provide a restricted administrator CIDR, VM/image settings and TLS contact in private configuration. Point each selected domain and alias at the VM before certificate issuance.
bash init-deploy-database-api.sh
bash run.sh --13 --staging --plan
bash run.sh --13 --staging --preflight
Review deployment.targets.json and protected runtime settings before cloud changes. The default environment is production; always specify --staging for staging work.
Production hostname
Set production:13 domain to asi-database-node-api.arybit.co.ke and review its www alias. VM version 13 does not require a versioned public hostname. Changing an example does not modify a running VM or DNS. Existing private target files must be reviewed separately. The target domain drives allowed hosts, CORS, Nginx server names and certificate paths. Keep ui/config.json apiBase empty to follow the served HTTPS origin.
Deploy and verify
bash run.sh --13 --plan
bash run.sh --13 --preflight
bash run.sh --13 --core
bash run.sh --13 --verify
bash run.sh --13 --logs --stream
Automatic deploy/core may authenticate to Azure, publish source to main and wait for exact-commit CI. Use --no-auto for manual preparation. These are operator commands, not steps performed by documentation generation. Deployment does not apply product SQL migrations.
Phase responsibilities
| Phase | Purpose |
|---|---|
| 01 | Infrastructure bootstrap |
| 02 | AI runtime core |
| 03 | Database stack |
| 04 | Backend deployment |
| 05 | Frontend deployment |
| 06 | Reverse proxy gateway |
| 07 | TLS certificate management |
| 08 | Production hardening |
| 09 | Health verification |
| 10 | Observability |
| 11 | Recovery daemon |
| 12 | Lifecycle management |
| 13 | Release promotion/update |
| 14 | Backup/disaster recovery |
| 15 | Cleanup/reclaim |
| 16 | Diagnostics/repair |
The coordinator selects the execution graph; phase scripts delegate to remote.py. Review bash run.sh --help for supported combinations. Candidate readiness and public HTTPS verification precede acceptance. Generated HTML ships with ui/ and is served at /admin/doc/ through the existing Nginx alias.
Rollback, backup and cleanup
Use the maintained rollback-release.sh entry point after reviewing its help and target. Rollback changes releases; it does not undo schema migrations. Encrypted backup requires an age recipient and a separate backup_env_file. Restore into an isolated destination with ops/restore-backup.py, then verify data and permissions before recovery. See operations.
Cleanup requires an explicit version, a reviewed --clean --plan, --confirm-clean and production --allow-production-destruction. A preserve target cannot be deleted. Never clean the existing v13 production group.
Local edits and publication
Automatic publication and deployment use the same source cleanliness policy. Unstaged changes to the root deployment.targets.json and asi-database-node-api.sh are local operator settings and do not block source publication or release transfer. Neither file is shipped in the application archive. Staging either file is rejected; all other uncommitted or untracked source still blocks the final deployment gate. The launcher is not automatically edited or committed.
A routine deployment can reuse the existing VM with
bash run.sh --13; cleanup is not required. To retry application deployment on an
already prepared VM, use bash run.sh --13 --core. Review the selected target first.
HTTP certificate challenge failures
If Certbot reports a connection error fetching /.well-known/acme-challenge/, the candidate may already be running while public HTTPS acceptance remains incomplete. Deployment and --core retries reconcile the owned VM NSG rules for TCP 80 and 443, while SSH remains restricted to admin_cidr. Existing subnet-level rules, higher-priority denies and host firewall rules can still prevent public access and require review.
During issuance, missing hostnames receive a temporary HTTP challenge-only server. The active application proxy remains intact. A loopback HTTP probe checks each hostname's challenge content before Certbot runs; this does not prove public access. Temporary routes and probe files are removed on success or failure.
After reviewing the target, retry with bash run.sh --13 --core. No cleanup is
required. If local challenge checks pass but issuance still fails, verify public
TCP 80 reachability, effective NIC/subnet NSG rules, host firewall rules and A/AAAA
records before retrying. Do not disable certificate validation.
Enable application operations independently
Managed v14 installs MySQL 8.0 and its versioned auth stream; existing v13 keeps PostgreSQL. PostgreSQL application operations use the independent postgresql-auth stream. Use DATABASE_ENGINE and reviewed credentials consistently with the target. No engine setting converts existing data. See v14 migration and acceptance.
Readiness and certificate checks alone do not constitute functional acceptance. After separately creating a reviewed fixture user, run this read-only check with a service key that has users:read in its tenant:
python tools/verify-application.py --base-url https://REVIEWED_STAGING_HOST --credential-file /secure/staging-service.key --user-id FIXTURE_UUID
It verifies a successful authorized lookup and a 401 for an unauthenticated lookup, with certificate and hostname verification, no redirects and a bounded timeout. It does not write production data. Staging must additionally exercise user/session writes, persistence, foreign-tenant rejection and reset replay with disposable fixtures, then reconcile the actual PHP contract. Those live staging operations were not run in this audit. Rollback of API code does not revert MySQL DDL or user data. Preserve the prior runtime configuration and schema-compatible release.
Dual-interface listener and public-IP mobility
The revised transport supports verified-TLS remote MySQL and an explicit DIRECT_DATABASE_ENABLED=1 path through existing deployment validation. It does not create a MySQL server merely from a hostname. The optional independent installer accepts a reviewed generated listener config; it defaults to a read-only plan:
bash deploy-database-api/ops/install-mysql-listener.sh --engine mysql --config /secure/direct-plan/mysqld-tls.cnf
After verified backup/restore, certificate installation and maintenance review, adding --apply installs/validates the selected engine and configuration, with a rollback attempt on service restart failure. It does not migrate schema, open NSGs, create accounts or convert an existing different database engine. Ubuntu package availability must match the qualified engine line; no vendor script is downloaded. The host installer was syntax checked, not run against production or a VM.
Use separate administrator migration credentials with a retained non-root DB_ROUTINE_DEFINER for the shared routines/audit trigger. Install the additive migrations before deploying the new API. Add service-limit table rights and shared routine EXECUTE grants to the HTTPS runtime. Provision distinct direct users via ops/provision-direct-account.py and independent HTTPS identities via backend/service-identity.py. Keep protected CA bundles readable by the API service; server private keys belong only to the database service/operator.
Public connectivity plan records the observed Azure configuration and exact proposed port-3306 rule. Public reachability is explicitly requested for any client IP; it still requires TLS-only execute-only accounts, connection caps and reviewed authentication-abuse monitoring. No NSG change was applied. Existing NSG SSH restrictions must remain intact. Generated /setup-api/ gateway routes now deny non-loopback clients; use a pinned SSH HTTPS tunnel for operator actions and preserve hostname verification. Existing live gateway settings have not been changed. See dual-interface runbook.