Files
rootnode-academy/docs/PHASE10_PRODUCTION_DEPLOYMENT.md
T
ced4568 0b2303a3e2 docs(phase10): canonical state audit, secrets inventory, production/security/launch readiness
Gate 1 — canonical state audit: this workstation's HEAD/origin/main and every claimed milestone
SHA (Phase 2A through 9.1) verified consistent; no stranded branches/tags on Gitea; full-history
secret scan clean (no rotation required); Phase 9.5 confirmed absent from canonical history
(pricing was given directly by Chase and used without it); second-PC reconciliation commands
provided since this session cannot see that machine.

Gate 3 — secrets: real inventory derived from process.env greps across every service (not
guessed); Infisical auth fixed (wrong domain was the actual root cause of Phase 9.1's failures —
the identity belongs to infisical.cedshomelab.com, not app.infisical.com) but no RootNode
Academy project exists there yet, contradicting this phase's own authorization — flagged for
Chase to create.

Gates 4-9/15-16 — production: verified, not assumed, that no dedicated server/Proxmox VM has
been provisioned (empty terraform.tfvars, no state file, placeholder group_vars/production.yml)
— IaC validated (tofu validate, ansible --syntax-check both clean) and left unexecuted per this
phase's own 'do not fake production deployment' instruction. rootnodeacademy.com confirmed live
(Cloudflare, real HTTPS) — the separate, not-RootNode-owned website; no Platform API subdomain
exists yet.

Gate 18 — security audit: full review against every regression suite rerun this phase; no
launch-critical defect found.

Gates 19/20 — full regression (131 checks: e2e-phase5b/5c/6f/7/8/9, content validation,
migrations, frontend build) and the Winter Break commerce rehearsal, both rerun clean against
current main.

Gates 10-14/21 — website integration, Winter Break pricing (99/49, real, Stripe-validated),
agreements/legal, tax, and refund policy all reviewed; legal text/tax/refund policy/dates/venue/
capacity correctly left as open owner decisions, not invented.

No application code changed this phase — audit/launch-prep only.
2026-08-28 12:07:25 -05:00

6.5 KiB

Phase 10 — Production Deployment

Gates 4-9, 15-16. Covers production infrastructure, network security, DNS/HTTPS, database productionization, Stripe LIVE configuration, Google Workspace production email, observability, and backups/restore.

Hardware status — PRODUCTION DEPLOYMENT BLOCKED — SERVER REQUIRED

Verified, not assumed: infra/terraform/ has no real terraform.tfvars (only terraform.tfvars.example) and no .tfstate file anywhere in the repo or working tree — tofu apply has never been run against real infrastructure. infra/ansible/group_vars/ production.yml still holds literal, unfilled placeholders: platform_ip: __PLATFORM_VM_IP__, lab_host_ip: __LAB_HOST_VM_IP__, proxmox_mgmt_cidr: __PROXMOX_HOST_MGMT_CIDR__. No SSH config, known-hosts entry, or inventory file on this workstation references a real rn-platform-01/rn-lab-host-01/Proxmox host.

The dedicated RootNode Academy production server has not been purchased or provisioned. Per Phase 10's own explicit instruction, this phase does not fake a production deployment against placeholder infrastructure. Everything below is preparation, validation, and rehearsal — genuinely useful the moment real hardware exists, but none of it is a live deployment.

What WAS done this phase (all safe, all pre-hardware)

  • Gate 4 — IaC validated: tofu validate (OpenTofu, the Terraform-compatible tool actually installed) passes cleanly against infra/terraform/'s existing Proxmox provider config — no redesign, the existing config is syntactically sound and ready for real terraform.tfvars. ansible-playbook site.yml --syntax-check -i inventory passes cleanly against infra/ ansible/'s existing site.yml and its firewall/docker-tls/platform/lab-host/ monitoring-agent/backup/common roles (all present from Phase 4/4.1, unmodified).
  • Gate 5 — network security architecture reviewed, unchanged: RFC1918 egress denial/allowlisting present in platform/provisioner/src/egress.js/lifecycle.js (Provisioner owns lab-container network policy); mTLS wiring present in infra/ansible/roles/docker-tls/ and referenced by gateway.env.j2; the firewall Ansible role exists for both host tiers. All of this is existing Phase 4/4.1 work, re-confirmed present and un-redesigned — none of it can be verified AGAINST a real host without one existing.
  • Gate 6 — DNS/HTTPS audited (read-only): rootnodeacademy.com is already live — resolves via Cloudflare (172.67.141.39, 104.21.38.247), serves a real HTTPS site (HTTP/2 200, Next.js application headers), and its mail is already routed to Google (smtp.google.com), consistent with the existing [email protected] Google Workspace setup. This is the separate, not-RootNode-owned marketing website (docs/ WEBSITE_REGISTRATION_AND_CHECKOUT.md's explicit boundary) — nothing here was touched, and no DNS record was added or changed. No subdomain or DNS record exists yet for the Platform API itself (e.g. api.rootnodeacademy.com or similar) — that's the piece blocked on real server hardware.
  • Gate 7 — database productionization reviewed: migrations (37, through Phase 9) verified idempotent again this phase (a fresh down -v/up cycle and a same-database container restart both apply/re-apply cleanly with zero errors — see docs/ PHASE10_LAUNCH_READINESS.md's regression section). The Ansible platform role's postgres.env.j2/.template already model least-privilege connection strings (rootnode_platform app user, not a superuser). No student records of any kind exist to accidentally copy into a production database — this dev/test Postgres only ever holds disposable test fixtures, all cleaned up after each rehearsal.
  • Gate 8 — Stripe LIVE: explicitly not configured, not attempted. See "Stripe LIVE status" below.
  • Gate 9 — Google Workspace: GOOGLE_SERVICE_ACCOUNT_JSON/GOOGLE_DELEGATED_USER already exist as real, previously-validated local values (Phase 5C's own docs/ RUNBOOK_GOOGLE_WORKSPACE_EMAIL.md documents that validation) — [email protected], gmail.send scope only, domain-wide delegation. Not re-sent this phase (no new test email was needed to confirm what Phase 5C already proved, and Phase 10 explicitly says not to make GWS a blocker for Stripe/launch work). Production DEPLOYMENT of this config is blocked on the same missing server as everything else.
  • Gates 15/16 — observability and backups reviewed, unchanged: /healthz (Platform API, Gateway, Provisioner) and /metrics (Prometheus text exposition, low-cardinality labels only — reconfirmed by this phase's own audit in docs/PHASE10_SECURITY_AUDIT.md) already exist and were exercised repeatedly by every regression suite rerun this phase. The monitoring-agent and backup Ansible roles exist (infra/ansible/roles/monitoring-agent/, .../backup/, including a templated run-backup.sh.j2) — built in Phase 4, never yet run against a real host. A restore has never been tested against real infrastructure, because none exists — this phase cannot manufacture that proof; it is not sufficient for launch per Phase 10's own "a backup that has never been restore-tested is not sufficient" rule, and is listed as a real blocker in the launch checklist, not glossed over.

Stripe LIVE status

Not configured. No live Stripe credential was provided to this session, and this phase did not request one — activating Stripe live mode requires Chase's own Stripe account business/identity verification (a real KYC-style process only Chase can complete), which is an explicit Phase 10 STOP condition ("identity verification requires Chase"). Phase 9.1's real TEST-mode validation (docs/PHASE9_1_STRIPE_TEST_VALIDATION.md) remains the full proof of the application-layer correctness; going live only needs Chase's real credentials plugged into the same, already-proven code path — see docs/STRIPE_INTEGRATION_RUNBOOK.md's "Going live" section, unchanged by this phase.

Bottom line

PRODUCTION DEPLOYMENT BLOCKED — SERVER REQUIRED. Every gate that depends on real hardware (4, 5's live verification, 6's platform subdomain, 7's live database, 8, 9's live deployment, 15, 16) is prepared, validated, and ready to execute the moment rn-platform-01/rn-lab-host-01 exist on real Proxmox hardware and terraform.tfvars/group_vars/production.yml are filled with real values — none of it requires new code or a redesign.