v1.1.0 — single-origin prod + HTTP Basic auth (Cloudflare quick-tunnel ready)
- **Single-origin prod mode.** When `WEB_STATIC_DIR` env var is set, FastAPI serves the React `web/dist/` build at `/` via `StaticFiles(html=True)`. Mounted after every `@app.get`…
Added — deployment infrastructure (post-1.0 backlog item 2, partial)
- Single-origin prod mode. When
WEB_STATIC_DIRenv var is set, FastAPI serves the Reactweb/dist/build at/viaStaticFiles(html=True). Mounted after every@app.getso the API routes stay intact. In dev (WEB_STATIC_DIRunset) the Vite dev server keeps doing what it does, no behavior change. - HTTP Basic auth middleware in
api/main.py, gated onAUTH_USERNAME+AUTH_PASSWORDenv vars being non-empty. Both unset → middleware is a no-op (dev + test behavior preserved)./healthzalways bypasses auth for monitoring probes.secrets.compare_digestfor the credential check. api/config.pygrew three settings:web_static_dir,auth_username,auth_password. Pydantic-settings handles.envand process env identically.scripts/run_prod.ps1— PowerShell runner: confirmsweb/dist/exists (auto-builds if not), setsWEB_STATIC_DIR, warns ifAUTH_*env unset, starts uvicorn on:8000, opens a Cloudflare quick tunnel (cloudflared tunnel --url) and prints the*.trycloudflare.comURL. Ctrl-C cleans up both processes.- 8 new auth-middleware tests (
tests/test_auth_middleware.py): /healthz bypass / missing-auth 401 / wrong password / wrong username / correct creds pass / malformed header / invalid base64 / disabled-when-unset. 295 tests passing (287 → 295).
Why these choices
- HTTP Basic over Cloudflare Access — Access requires a domain on Cloudflare DNS, which we don't have yet. Once a named tunnel + domain land (post-quick-tunnel), unset
AUTH_*and Access fronts auth instead — no code change. - Single-origin — one tunnel target, no CORS in prod, no separate CDN for the React bundle. The CORS middleware stays in place for the dev-server scenario (Vite on :5173 hitting FastAPI on :8000).
- PowerShell runner — dev box is Windows. A
cronequivalent would be Task Scheduler, but the user chose manual Poll-now over scheduled polling so no cron is wired in.
Operator steps to actually ship a URL
- Add to
.env:WEB_STATIC_DIR=d:\Misc\Vigil\web\dist,AUTH_USERNAME=<pick>,AUTH_PASSWORD=<long random>. npm run buildinweb/once (or let the script do it)..\scripts\run_prod.ps1from project root. Bookmark the*.trycloudflare.comURL printed in the cloudflared output.- Stable URL needed → file an is-a.dev PR (~24-48h merge) or buy a $10/yr domain; then upgrade to a named tunnel + Cloudflare Access (and unset
AUTH_*).