feat(web-studio): auto-proxy mode for public deployments#2312
feat(web-studio): auto-proxy mode for public deployments#2312ZaynJarvis wants to merge 2 commits into
Conversation
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
A new optional Node.js backend (`web-studio/server/proxy.mjs`) and a runtime config endpoint (`/_studio/runtime-config.json`) let Web Studio be deployed as a public site without exposing the OpenViking root API key to the browser: - Browser talks to a same-origin proxy with no `X-API-Key` of its own. - Proxy injects server-managed `X-API-Key` (and optional account / user) before forwarding to an upstream OV server configured via env vars. - Incoming `X-API-Key`, `Authorization`, `X-OpenViking-Account`, and `X-OpenViking-User` headers are stripped before forwarding so clients cannot override the server-managed identity. - The SPA fetches `/_studio/runtime-config.json` at boot; when `proxyMode` is true it hides the connection dialog form, suppresses 401-driven reopen, and stops persisting credentials in `localStorage` / `sessionStorage`. The dialog shows a read-only "auto-proxy mode" notice. Zero-dep Node script + ~60 lines of SPA wiring. nginx layout in the main README remains the preferred answer when nginx is already in the path; this mode targets single-process "static frontend + thin proxy" deployments.
- `Dockerfile`: multi-stage `node:22-alpine` image (~150 MB). Build stage runs `npm ci` + `npm run build`; runtime stage ships only `dist/`, `server/`, and `package.json`. Zero-dep proxy means no production node_modules. - `.dockerignore`: keeps node_modules / dist / .git / docs out of the build context. - `railway.toml`: pins Dockerfile build, healthcheck on `/_studio/runtime-config.json`, restart-on-failure with 5 retries. - `server/proxy.mjs`: honor `PORT` env (Railway / Fly / Render / Heroku convention) before falling back to `OV_STUDIO_PORT` / `3000`. - `server/README.md`: Docker + Railway / Fly / Render quick-start sections. Local docker build is blocked by the host machine's full disk and corrupted docker daemon, so the image is unverified locally — Railway will build it server-side. The Dockerfile is small and standard, and the proxy.mjs has already been smoke-tested with the `PORT` env var.
de68539 to
1599f4c
Compare
|
Rebased onto current
Verified after rebase: Also added a Dockerfile + Railway deploy path so this can be smoke-tested end-to-end:
Railway path (per Note: local |
Summary
Adds an optional auto-proxy mode to Web Studio so a single deployment can be exposed publicly without ever shipping the OpenViking root API key to the browser.
web-studio/server/proxy.mjsserves the built SPA and proxies/api,/bot,/health,/ready,/openapi.jsonto a configured upstream OV server, injectingX-API-Key(and optionalX-OpenViking-Account/X-OpenViking-User) server-side.X-API-Key,Authorization,X-OpenViking-Account,X-OpenViking-Userare stripped before forwarding — browser callers cannot override the server-managed identity./_studio/runtime-config.jsonendpoint tells the SPA it is in proxy mode. The SPA then:X-API-Keyfrom the browser,localStorage/sessionStorage,All upstream addressing and credentials live in env vars (
OV_STUDIO_UPSTREAM,OV_STUDIO_API_KEY, ...). Seeweb-studio/server/README.mdfor the full env reference and threat model.Usage
Files
web-studio/server/proxy.mjs— proxy server (zero deps, ~325 LOC, Node built-ins only)web-studio/server/README.md— env reference + threat modelweb-studio/src/lib/studio-runtime.ts— boot-time fetch of/_studio/runtime-config.jsonweb-studio/src/lib/ov-client/{client,types}.ts— newproxyModeoption that suppressesX-API-Keyheader injectionweb-studio/src/hooks/use-app-connection.tsx— proxy-mode aware persistence / interceptorweb-studio/src/components/connection-dialog.tsx— read-only notice in proxy modeweb-studio/src/main.tsx— awaits runtime config before bootstrapweb-studio/src/i18n/locales/{en,zh-CN}.ts—connection.proxyMode.*+common.action.closeweb-studio/{README,README_CN}.md— new "Auto-Proxy Mode" deployment sectionTest plan
npm run lint— 0 errors (the 15 remaining warnings are all pre-existingi18next/no-literal-stringinroutes/resources/-components/file-preview.tsx).npm run build— succeeds, bundle contains_studio/runtime-config.jsonandproxyModereferences.npm run proxysmoke test against a stub upstream:GET /_studio/runtime-config.json→{"proxyMode":true,...}.GET /→ builtindex.html.GET /some/spa/route→ SPA fallback (200 withindex.html).POST /api/v1/search/findwith attacker-suppliedX-API-Key,Authorization,X-OpenViking-Account: client headers stripped, server injectsX-API-Key=injected-key+X-OpenViking-Account=acme+X-OpenViking-User=alice+X-OpenViking-Agent=web-studio-proxy; POST body forwarded intact.502 {"status":"error","error":{"code":"UPSTREAM_UNREACHABLE",...}}(matches OV error envelope shape).openviking-server --with-botupstream (deferred — submitting as draft for design feedback first).oauth-setup-dialogUX needs a proxy-mode adjustment (currently it still renders withconnection.apiKey="", which falls through to the "custom apiKey" mode — out of scope for this PR but flagged).Design notes / open questions
web-studio/server/, mirroring the existing nginx layout in the README — no changes toopenviking-server. Open to moving it into a separate package (@openviking/web-studio-proxy) if you'd prefer it published independently.OV_STUDIO_PROXY_PATHS=/api,/bot,/health,/ready,/openapi.jsonmatches what Web Studio actually calls. Future bot websocket support would need anupgradehandler — easy to add but skipped for now sinceopenviking-serverhas no/wsendpoint today.upgradeevent handler.OV_STUDIO_ACCOUNT_ID/OV_STUDIO_USER_IDenv vars let a deployment pin to a specific account/user. Without them, the upstream applies its own default identity attached to the root key.🤖 Generated with Claude Code