Add session summary — hosted IDE via Traefik wildcard curl-verified

This commit is contained in:
jester 2026-03-22 21:56:41 +00:00
parent c499be3e1e
commit f1270c8ede

View File

@ -0,0 +1,107 @@
# 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