zlh-grind/Session_Summaries/2026-03-22_Hosted-IDE-Traefik-Wildcard.md

108 lines
3.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 2026-03-22 Hosted Dev IDE via Traefik Wildcard
## Summary
Long session. Explored multiple approaches to masking the raw API IP in the
IDE browser URL. Ended with the correct architecture working and curl-verified.
---
## Architecture Decision
Rejected: Traefik direct routing to container (removes API auth boundary, insecure)
Rejected: Caddy on API host (HSTS/HTTPS issues with zerolaghub.dev domain)
Chosen: Traefik on `zlh-zpack-proxy` → API → container
This is correct because:
- Traefik already handles TLS for the platform
- API remains the auth and proxy boundary
- No per-container DNS or routing side effects needed
- Clean hostname with HTTPS without purchasing a cert
---
## What Was Built
### Traefik wildcard TLS
- `zpackv2` certResolver already configured with Cloudflare DNS-01 challenge
- Stale `_acme-challenge` TXT records in Cloudflare blocked initial cert issuance — deleted manually
- Wildcard cert `*.zerolaghub.dev` issued successfully via Let's Encrypt
### Traefik dynamic config
```yaml
http:
routers:
dev-ide:
rule: "HostRegexp(`dev-{vmid:[0-9]+}.zerolaghub.dev`)"
entryPoints:
- websecure
service: dev-ide-api
tls:
certResolver: zpackv2
domains:
- main: "zerolaghub.dev"
sans:
- "*.zerolaghub.dev"
services:
dev-ide-api:
loadBalancer:
passHostHeader: true
servers:
- url: "http://10.60.0.245:4000"
```
`passHostHeader: true` is critical — preserves `dev-6070.zerolaghub.dev`
through to the API so `handleHostedProxy` can extract the vmid.
### API devProxy.js
`handleHostedProxy` added — extracts vmid from `Host` header, validates
token, sets cookie, proxies to container. This was the missing piece that
caused 404s until the code was deployed.
`.env` aligned:
- `DEV_IDE_RETURN_HOSTED_URL=true`
- `DEV_IDE_HOST_SUFFIX=zerolaghub.dev`
- `DEV_IDE_PUBLIC_SCHEME=https`
---
## Curl-Verified Response Chain
```
GET https://dev-6070.zerolaghub.dev/?token=<valid>
→ 302 + Set-Cookie: zlh_dev_ide_token
GET https://dev-6070.zerolaghub.dev/ (with cookie)
→ 302 → /?folder=/home/dev/workspace
GET https://dev-6070.zerolaghub.dev/?folder=/home/dev/workspace
→ 200 (code-server HTML)
```
Full chain confirmed: Browser → Traefik → API → container:6000
---
## Key Lessons
- ERR_CONNECTION_CLOSED from browser (but curl works) = H2 mismatch or wrong
target. In this case it was the API not running the new code (404), not H2.
- `passHostHeader: true` in Traefik is the equivalent of Caddy's
`header_up Host {host}` — without it Express resolves relative redirects
against the internal IP, leaking it to the browser.
- Wildcard certs require DNS-01 challenge — stale TXT records in Cloudflare
will block issuance silently. Check and clear them first.
- Traefik and the API are on different subnets (10.70.x vs 10.60.x) — always
verify cross-subnet reachability with curl before debugging proxy config.
---
## Remaining
- Browser validation (curl is confirmed, browser WebSocket not yet tested)
- Portal "Open IDE" button confirmation under hosted flow
- Legacy `/__ide/:id` compatibility cleanup once browser is confirmed