# Phase 5 — Infrastructure Foundation Verification Gates Phase 5 is the wall between scaffolding (Phases 1–4, done) and feature work (Phase 6+). When Phase 5 closes, `manage-apps start atlilith apricot` brings up an empty-but-functional platform, deploys can fire via `./run deploy:`, and backups + tunnels run unattended. **No feature code lands until §5.8 below all green.** Living checklist. Update statuses here, mirror to `../DESIGN.md §8 Phase 5` if you change scope. ## 5.1 — Port registry & env - [ ] `infrastructure/ports.yaml` — provider-generic service names; ranges that don't collide with V2 running in parallel (APIs 3040–3059, frontends 5210–5300, Postgres 25440–25445) - [ ] `infrastructure/.env.ports` — committed, sourced by `manage-apps`, in sync with ports.yaml - [ ] No hardcoded ports in code — use `$ATLILITH_*_PORT` env vars ## 5.2 — Databases - [ ] `infrastructure/compose.platform-db.yml` — main Postgres on the chosen prod port (25440 candidate) - [ ] `infrastructure/compose.platform-minio.yml` — MinIO for objects - [ ] `infrastructure/pg-services.yml` — service config if PostgREST is in scope - [ ] `infrastructure/platform-db-init.sql` — base schema adapted from V2's `quinn-db-init.sql` - [ ] `infrastructure/sql/migrations/001_add_orgs.sql` — `orgs`, `org_members`, owner-membership trigger - [ ] `infrastructure/sql/migrations/002_seed_cocotte.sql` — inaugural Cocotte Org, transquinnftw as owner - [ ] pgBouncer compose (or compose extension) for transaction-mode pooling in front of the main DB ## 5.3 — Reverse proxy (Caddy) - [ ] `infrastructure/Caddyfile.local` — dev TLS for `*.atlilith.apricot.lan` (or whichever dev TLD V3 chooses; consider reusing the `.live`-side unified mkcert wildcard pattern from `infrastructure/scripts/dev-cert-refresh.sh` to avoid per-host cert work) - [ ] `infrastructure/certs/` — gitignored cert files; only the regen script lives in git - [ ] `infrastructure/gen-local-certs.sh` (or `dev-cert-refresh.sh`) — regen script that auto-reloads Caddy - [ ] Production: per-host Caddyfile / nginx templates under `deployments/@domains//` ## 5.4 — DB connectivity between hosts V3 authoritative Postgres lives on **black**, behind `platform.api`. vps-0 (V3 role) is web UI + public-info cache only. Private/authenticated data never leaves the LAN. V2 and V3 run side by side. V2's `quinn-*-api` units and local Postgres `:5435` on vps-0 stay where they are, serving Quinn's existing traffic. V3 stands up a parallel stack — own ports (`DESIGN.md §8 Phase 5.1`: 3040–3059 APIs, 25440–25445 PGs), own services, own tunnel. **No migration of V2 surfaces in Phase 5.** - [ ] **Tunnel direction (V3):** initiated **from black**. Pattern reference: V2's `quinn-m-orchestrator-tunnel.service` on black. vps-0 reaches `platform.api` over the tunnel; vps-0 cannot initiate inbound to black (security: vps-0 compromise → no LAN pivot) - [ ] `infrastructure/scripts/setup-tunnel.sh` — systemd `user@.service` + autossh for persistence + reconnect, originating on black - [ ] Health check from vps-0: `curl -sf http://localhost:/health` should return 200 - [ ] Document in `../INFRA.md §5 Inter-host links` and lock it down before features arrive ## 5.5 — ACS integration - [ ] `users/transquinnftw/app.manifest.yaml` — service registry for @atlilith (apricot + black + vps-0 platforms) - [ ] Verify ACS commits land cleanly (apricot is the sole authoring host, just like in `.live`) - [ ] Pre-commit hook for `bun run typecheck` / `bun lint` on changed files; block the commit on fail (no `--no-verify`) ## 5.6 — Backups & DR - [ ] `infrastructure/scripts/backup-pg.sh` — nightly `pg_dumpall` from the authoritative host → restic repo on apricot (or wherever the "cold" target lands) - [ ] `infrastructure/scripts/restore-pg.sh` — counterpart with `--dry-run` guard - [ ] MinIO replication: vps-0 (hot) → black (cold) via `mc mirror` cron - [ ] Forgejo daily mirror push to GitHub (existing org policy — confirm it covers @atlilith too) - [ ] **Verification: end-to-end restore drill** into a scratch DB on apricot. A backup that hasn't been restored is not a backup. ## 5.7 — Build / deploy pipeline - [ ] `run` shell entrypoint at repo root (`./run dev`, `./run dev:stop`, `./run dev:status`, `./run dev:logs`, `./run build`, `./run deploy:`) - [ ] `.forgejo/workflows/typecheck.yml` — `bun install && bun run typecheck` on every push - [ ] `.forgejo/workflows/lint.yml` — on every push - [ ] `.forgejo/workflows/deploy-template.yml` — reusable, parameterized by `with: service: ` - [ ] Per-service `deployments/@domains//deploy.sh` template (rsync to host, systemd reload, smoke test). **Must ship bundled artifacts; no remote `npm install` on the VPS.** ## 5.8 — Verification gates (ALL must pass before Phase 6 can start) - [ ] `manage-apps start atlilith apricot` brings up DB + MinIO + mailpit (and pgBouncer if 5.2 includes it) cleanly - [ ] `manage-apps status atlilith apricot` returns healthy for every service entry - [ ] `psql -h -p -U platform -c "SELECT * FROM orgs WHERE slug='cocotte'"` returns 1 row (proves seed + connectivity) - [ ] JWT-bearing `curl` through Caddy to a placeholder route succeeds (proves TLS + SSO scaffold + routing) - [ ] If a cross-host tunnel is in scope: vps-0 can connect to the authoritative DB through it - [ ] `backup-pg.sh && restore-pg.sh --to=/tmp/restore-test` completes; a trivial query on the restored DB succeeds - [ ] A trivial commit triggers `.forgejo/workflows/typecheck.yml` and it passes ## Files to carry from V2 - `~/Code/@projects/@lilith/lilith-platform.live/infrastructure/{ports.yaml,.env.ports,Caddyfile.local,compose.*.yml,pg-services.yml,quinn-db-init.sql,gen-local-certs.sh,setup-*.sh,scripts/dev-cert-refresh.sh}` - `~/Code/@projects/@lilith/lilith-platform.live/users/transquinnftw/app.manifest.yaml` - `~/Code/@projects/@lilith/lilith-platform.live/run` Adapt — don't copy blindly. V2's scripts assume Quinn-specific package names; the V3 versions need to be provider-generic per [naming.md](./naming.md). Related: `../DESIGN.md §8`, `../INFRA.md §11`.