diff --git a/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md b/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md index 293d3c25..04a2c557 100644 --- a/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md +++ b/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md @@ -1,4 +1,4 @@ -# Track A: Azure VM — Gateway, Dashboards & Security +# Track A: Azure VM — Gateway, Gitea & Security > **Agent:** Codex on Azure VM (`/opt/bytelyst/`) > **Prompts:** 4 (run in dependency order below) @@ -17,34 +17,35 @@ | --- | ------------------------ | :------------: | :--------: | :------: | | A1 | Caddy Gateway Setup | 🟨 In progress | — | ⬜ | | A2 | Gitea HTTPS Exposure | ⬜ Not started | — | ⬜ | -| A3 | Dashboard Containers Fix | ⬜ Not started | — | ⬜ | +| A3 | Dashboard Frontend Split | ⬜ Not started | — | ⬜ | | A4 | NSG Lockdown | ⬜ Not started | — | ⬜ | **Execution order:** ``` A1: Caddy Gateway ──→ A2: Gitea HTTPS ──→ A4: NSG Lockdown - └──→ A3: Dashboard Fix (parallel with A2) + └──→ A3: Dashboard Frontends on Vercel ``` --- ## DNS Prerequisites (manual — do before starting) -These DNS records must exist before Caddy can obtain Let's Encrypt certificates: +These VM-hosted DNS records must exist before Caddy can obtain Let's Encrypt certificates: | Record | Type | Value | Purpose | | ---------------------- | ---- | ---------------------- | ------------------- | | `api.bytelyst.com` | A | `` | Backend API gateway | | `gitea.bytelyst.com` | A | `` | Gitea npm registry | -| `admin.bytelyst.com` | A | `` | Admin dashboard | -| `tracker.bytelyst.com` | A | `` | Tracker dashboard | +| `admin.bytelyst.com` | CNAME or A | `` | Admin frontend | +| `tracker.bytelyst.com` | CNAME or A | `` | Tracker frontend | **Verify DNS before starting A1:** ```bash dig +short api.bytelyst.com # must return VM IP dig +short gitea.bytelyst.com # must return VM IP +# admin/tracker should resolve to Vercel, not the VM ``` --- @@ -52,7 +53,7 @@ dig +short gitea.bytelyst.com # must return VM IP ## A1: Caddy Gateway — Replace Traefik with Caddy > **Depends on:** DNS records for `api.bytelyst.com` -> **Blocks:** A2, A3, A4 +> **Blocks:** A2, A4 ### Preconditions @@ -221,92 +222,70 @@ AFTER COMPLETING: Notify the operator that Track B → B4 (.npmrc migration) is --- -## A3: Fix admin-web and tracker-web Containers +## A3: Move admin-web and tracker-web to Vercel -> **Depends on:** A1 ✅ (Caddy running for subdomain routing) -> **Blocks:** Nothing (independent of Track B) +> **Depends on:** A1 ✅ for `api.bytelyst.com` +> **Blocks:** Nothing on the VM track > **Can run in parallel with:** A2 ### Preconditions -- [ ] A1 is complete — Caddy is running -- [ ] `admin.bytelyst.com` and `tracker.bytelyst.com` DNS A records resolve to VM IP -- [ ] Source code for dashboards is available on the VM +- [ ] `api.bytelyst.com` is live on the VM with HTTPS +- [ ] `admin-web` and `tracker-web` source code is available in the repo +- [ ] Vercel project(s) exist or can be created for the dashboards ### Prompt ``` -You are working on the ByteLyst single-Azure-VM Docker deployment at /opt/bytelyst/. +You are working on the ByteLyst deployment split where the Azure VM hosts only self-hosted infrastructure and backend APIs, while public frontend apps deploy on Vercel. -TASK: Resolve the remaining admin-web and tracker-web containers so they are running and healthy, then expose them via Caddy. +TASK: Move admin-web and tracker-web out of the VM hosting path and make them Vercel-hosted frontends that talk to the VM backend through https://api.bytelyst.com. CONTEXT: -- Per DEPLOYMENT_STATUS_2026-03-29.md, all 13 backend services are healthy -- admin-web and tracker-web are the two remaining UNRESOLVED containers -- Both are Next.js 16 apps from learning_ai_common_plat under dashboards/ -- Both use workspace:* dependencies resolved by pnpm -- Both use the --webpack build flag -- admin-web port: 3001, tracker-web port: 3003 -- Docker build context: dashboards/admin-web/ and dashboards/tracker-web/ +- The VM should host only self-hosted and backend services +- admin-web and tracker-web are Next.js frontends from learning_ai_common_plat under dashboards/ +- Their public domains should be: + - https://admin.bytelyst.com + - https://tracker.bytelyst.com +- Those domains should point to Vercel, not the VM +- Browser/API configuration should use the VM backend entrypoint at https://api.bytelyst.com TASK LIST: -- [ ] 1. Check current container status: - docker compose ps admin-web tracker-web - docker compose logs admin-web --tail 100 - docker compose logs tracker-web --tail 100 -- [ ] 2. Diagnose the build/runtime failure (likely causes): - - @bytelyst/* package resolution — may need .docker-deps/ tarballs - - Standalone entrypoint not found - - Missing env vars (COSMOS_ENDPOINT, COSMOS_KEY, JWT_SECRET) -- [ ] 3. Fix the identified issues -- [ ] 4. Rebuild and restart: - docker compose up -d --build admin-web tracker-web -- [ ] 5. Add Caddy routes for the dashboards to the Caddyfile: - admin.bytelyst.com { - reverse_proxy admin-web:3001 - } - tracker.bytelyst.com { - reverse_proxy tracker-web:3003 - } -- [ ] 6. Reload Caddy: docker exec caddy caddy reload --config /etc/caddy/Caddyfile -- [ ] 7. Update DEPLOYMENT_STATUS_2026-03-29.md to reflect the fix +- [ ] 1. Audit both dashboards for browser-visible backend URLs and replace VM-local assumptions with the public API hostname +- [ ] 2. Document the required Vercel env vars for both apps +- [ ] 3. Ensure `admin.bytelyst.com` and `tracker.bytelyst.com` DNS point to Vercel, not the VM +- [ ] 4. Remove dashboard host-routing assumptions from the VM/Caddy plan +- [ ] 5. Update deployment docs to record the frontend/backend split VERIFICATION (run all — every check must pass): - # Containers healthy - docker compose ps admin-web tracker-web - # Expected: both show "Up" and "healthy" (or at least "Up") + # DNS should no longer point at the VM + dig +short admin.bytelyst.com + dig +short tracker.bytelyst.com + # Expected: Vercel-managed resolution, not the VM public IP - # Local port access - curl -sf http://127.0.0.1:3001 | head -5 - curl -sf http://127.0.0.1:3003 | head -5 - # Expected: HTML response from each - - # HTTPS via Caddy + # Frontends load on Vercel curl -sI https://admin.bytelyst.com | head -3 - # Expected: HTTP/2 200 (or 302) - curl -sI https://tracker.bytelyst.com | head -3 - # Expected: HTTP/2 200 (or 302) + # Expected: HTTP 200 or 302 from Vercel-hosted apps - # All containers now healthy - docker compose ps | grep -E "(unhealthy|Exit)" | wc -l - # Expected: 0 + # VM backend stays the single API entrypoint + curl -sf https://api.bytelyst.com/platform/health | jq -r '.status' + # Expected: ok SUCCESS CRITERIA: -- Both admin-web and tracker-web containers are running -- Both respond on their ports (3001, 3003) -- Both accessible via HTTPS at admin.bytelyst.com and tracker.bytelyst.com -- DEPLOYMENT_STATUS doc updated with fix details -- Zero unhealthy containers in the full stack +- admin.bytelyst.com is served by Vercel +- tracker.bytelyst.com is served by Vercel +- Both frontends use https://api.bytelyst.com for backend access +- The VM no longer needs dashboard routing or public dashboard ports -COMMIT: fix(deployment): resolve admin-web and tracker-web containers +COMMIT: docs(architecture): move dashboards to Vercel and keep VM backend-only ``` --- ## A4: NSG Lockdown — Reduce Public Port Exposure -> **Depends on:** A1 + A2 + A3 all complete (all traffic routed through Caddy) +> **Depends on:** A1 + A2 complete > **Blocks:** Nothing > **Run this LAST in Track A** @@ -314,7 +293,6 @@ COMMIT: fix(deployment): resolve admin-web and tracker-web containers - [ ] A1 complete — all backends accessible via `api.bytelyst.com` - [ ] A2 complete — Gitea accessible via `gitea.bytelyst.com` -- [ ] A3 complete — dashboards accessible via `admin.bytelyst.com` and `tracker.bytelyst.com` - [ ] All services verified working through Caddy (no direct port access needed) ### Prompt @@ -324,8 +302,8 @@ TASK: Reduce the Azure NSG (Network Security Group) rules so only ports 80, 443, CONTEXT: - The Azure VM currently exposes many service ports publicly (3000-3300, 4003-4019, 8025, 8080, 10000, 11434) -- Caddy now handles ALL public HTTPS traffic on ports 80/443 -- All services (backends, Gitea, dashboards, monitoring) are accessed through Caddy subdomains +- Caddy now handles the VM-hosted public HTTPS traffic on ports 80/443 +- Only backend and self-hosted services should remain on the VM - Only SSH (22) and HTTP/HTTPS (80/443) should remain publicly open - This follows the recommendation in SECURE_API_EXPOSURE.md @@ -355,9 +333,6 @@ VERIFICATION (run all — every check must pass): curl -sf https://gitea.bytelyst.com/api/v1/version | jq '.version' # Expected: version string - curl -sI https://admin.bytelyst.com | head -1 - # Expected: HTTP/2 200 (or 302) - # Direct port access blocked for port in 3001 3003 3300 4003 4005 8025 8080 10000; do curl -sf --max-time 3 http://:$port && echo "FAIL: $port open" || echo "PASS: $port closed" @@ -413,7 +388,7 @@ done echo "--- Gitea npm registry ---" curl -sf https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | jq '.name' -echo "--- Dashboards ---" +echo "--- Dashboards (Vercel-hosted) ---" curl -sI https://admin.bytelyst.com | head -1 curl -sI https://tracker.bytelyst.com | head -1 diff --git a/docs/devops/vercel/TRACK_A_HANDOFF_2026-03-29.md b/docs/devops/vercel/TRACK_A_HANDOFF_2026-03-29.md index 84e56768..f0d82d95 100644 --- a/docs/devops/vercel/TRACK_A_HANDOFF_2026-03-29.md +++ b/docs/devops/vercel/TRACK_A_HANDOFF_2026-03-29.md @@ -2,6 +2,12 @@ This handoff captures the current state of Track A on the Azure VM at `/opt/bytelyst/`. +Architecture decision after this handoff: + +- The VM should host only self-hosted infrastructure and backend APIs +- `api.bytelyst.com` and `gitea.bytelyst.com` should point to the VM +- `admin.bytelyst.com` and `tracker.bytelyst.com` should be Vercel-hosted frontends, not VM-hosted services + ## What Was Completed - Fixed a blocking parse error in [`docker-compose.ecosystem.yml`](/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml) by removing a duplicate `efforise-web` service definition. @@ -24,14 +30,14 @@ This handoff captures the current state of Track A on the Azure VM at `/opt/byte ### 1. DNS is not ready -From the VM, these records did not resolve: +From the VM, these records did not resolve at the time of the original session: - `api.bytelyst.com` - `gitea.bytelyst.com` - `admin.bytelyst.com` - `tracker.bytelyst.com` -Without these, Caddy cannot obtain Let's Encrypt certificates and A1/A2/A3 HTTPS verification cannot pass. +For the revised architecture, only `api.bytelyst.com` and `gitea.bytelyst.com` must resolve to the VM for Track A. `admin.bytelyst.com` and `tracker.bytelyst.com` should resolve to Vercel instead. ### 2. Azure CLI is missing @@ -57,23 +63,20 @@ This blocks reliable rebuilds for A3 and potentially other services. ## Required Preconditions Before Resuming -1. Create DNS A records for: +1. Create or confirm VM DNS records for: - `api.bytelyst.com` - `gitea.bytelyst.com` - - `admin.bytelyst.com` - - `tracker.bytelyst.com` 2. Confirm they resolve to the VM public IP from the VM: ```bash dig +short api.bytelyst.com dig +short gitea.bytelyst.com -dig +short admin.bytelyst.com -dig +short tracker.bytelyst.com curl -sf https://api.ipify.org && echo ``` -3. Install Azure CLI and log in with permissions to manage the VM NSG. -4. Export a Linux-safe Gitea host before rebuilds, for example: +3. Point `admin.bytelyst.com` and `tracker.bytelyst.com` at Vercel, not the VM. +4. Install Azure CLI and log in with permissions to manage the VM NSG. +5. Export a Linux-safe Gitea host before rebuilds, for example: ```bash export GITEA_NPM_HOST=172.17.0.1 @@ -145,37 +148,10 @@ gitea-npm-registry ### A3 -1. Keep `GITEA_NPM_HOST` exported to the Docker-reachable host IP before rebuilding. -2. Rebuild only the dashboard services first: - -```bash -export GITEA_NPM_HOST=172.17.0.1 -docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem up -d --build admin-web tracker-web -docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem logs admin-web --tail 100 -docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem logs tracker-web --tail 100 -``` - -3. Add dashboard host blocks to `/opt/bytelyst/Caddyfile`: - -```caddy -admin.bytelyst.com { - reverse_proxy admin-web:3001 -} - -tracker.bytelyst.com { - reverse_proxy tracker-web:3003 -} -``` - -4. Reload Caddy and verify: - -```bash -curl -sf http://127.0.0.1:3001 | head -5 -curl -sf http://127.0.0.1:3003 | head -5 -curl -sI https://admin.bytelyst.com | head -3 -curl -sI https://tracker.bytelyst.com | head -3 -docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem ps | grep -E "(unhealthy|Exit)" | wc -l -``` +1. Do not treat `admin-web` and `tracker-web` as VM-hosted production surfaces. +2. Deploy them to Vercel and configure them to use `https://api.bytelyst.com`. +3. Point `admin.bytelyst.com` and `tracker.bytelyst.com` to Vercel. +4. Do not add dashboard routes to `/opt/bytelyst/Caddyfile`. ### A4 @@ -194,5 +170,5 @@ Suggested commits: - `feat(gateway): replace Traefik with Caddy for HTTPS path routing` - `feat(gateway): expose Gitea npm registry via HTTPS at gitea.bytelyst.com` -- `fix(deployment): resolve admin-web and tracker-web containers` +- `docs(architecture): move dashboards to Vercel and keep VM backend-only` - `chore(security): lock down Azure NSG to 22/80/443 only`