fix(web): secure-context-safe UUIDs + HTTPS compose overlay for LAN deployments - #864
fix(web): secure-context-safe UUIDs + HTTPS compose overlay for LAN deployments#864Poytr1 wants to merge 2 commits into
Conversation
Browsers expose crypto.randomUUID only on HTTPS or localhost, so a console served over plain HTTP from a LAN host (e.g. a NAS reached by IP) crashed on Playground send, cron creation, and the Telegram wizard. Route every call through lib/random-id.ts, which falls back to a manual v4 UUID built from getRandomValues (not secure-context gated) — the fallback must stay a real UUID because webchat frames validate turnId with z.string().uuid(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
compose.https.yaml fronts web, control-plane, and relay with Caddy and its internal CA (one HTTPS port per service, keyed on AGENTCONNECT_HTTPS_HOST) and rewrites the browser-facing PUBLIC_* URLs and CORS origin to match. Browsers grant crypto.randomUUID, crypto.subtle, and clipboard access only to secure contexts, so a console opened from another LAN device needs HTTPS; daemons are Node processes and keep dialing the plain HTTP/WS ports unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
The secure-context UUID helper is well scoped and all six browser call sites now use it. The HTTPS overlay also renders the expected browser-facing Web/API/Relay URLs. However, the overlay currently breaks the normal daemon onboarding path: PUBLIC_CP_URL is also used to generate daemon WebSocket commands, so the generated command dials Caddy's private-CA wss:// endpoint even though the daemon transport has no way to trust that CA by default. The documented root-certificate export command also cannot be run as written because the required host assignment was only scoped to the earlier up invocation.
Verification: git diff --check passed; docker compose config passed with AGENTCONNECT_HTTPS_HOST and confirmed the HTTPS environment values; the unset-host invocation failed as configured. Focused Vitest/typecheck/lint could not be rerun because dependencies are absent and the pinned pnpm download was unavailable from this environment.
sent by review-bot (Codex · gpt-5.6-sol) · open in session
|
|
||
| control-plane: | ||
| environment: | ||
| - PUBLIC_CP_URL=https://${AGENTCONNECT_HTTPS_HOST}:${AGENTCONNECT_CP_TLS_PORT:-8443} |
There was a problem hiding this comment.
[P1] Keep daemon onboarding usable with the internal CA. PUBLIC_CP_URL is not browser-only: daemonWsUrl() derives every Add/Regenerate daemon command from it, so this override produces wss://<host>:8443/daemon/ws. The shared ClientTransport supplies no CA option to ws, and the required Node 24 runtime uses bundled roots by default, so trusting Caddy's root in the browser or OS does not make that generated npx ... run command trust it. The daemon therefore reconnects forever in the advertised LAN setup. Please provide a separate plain daemon dial URL, generate/document the required Node CA configuration, or otherwise ensure the generated command can authenticate this endpoint.
| ``` | ||
|
|
||
| Open `https://<host>:3443` and trust the generated root certificate once per | ||
| device (export it with `docker compose -f compose.yaml -f compose.https.yaml cp |
There was a problem hiding this comment.
[P2] Carry the required host into the certificate-export command. The preceding AGENTCONNECT_HTTPS_HOST=... assignment applies only to the up process; it is not present for this later docker compose ... cp. Compose interpolates the overlay before running cp and aborts because AGENTCONNECT_HTTPS_HOST is required. Please repeat the assignment here or show a persistent --env-file workflow. The analogous command in compose.https.yaml also needs the overlay file set (and the host value).
Problem
Browsers expose
crypto.randomUUID()only in secure contexts (HTTPS or localhost). A console served over plain HTTP from a LAN host — a stack on a NAS opened by IP is the typical case — crashed on Playground send, cron creation, and the Telegram wizard. The rest of the console was already hardened for insecure contexts (clipboard calls are try/caught, the webhook HMAC snippet degrades to a placeholder); the unguardedrandomUUIDcall sites were the remaining hard failures.Changes
packages/web: secure-context-saferandomUUID()(lib/random-id.ts)crypto.randomUUIDwhen available, otherwise a manual v4 UUID built fromcrypto.getRandomValues(not secure-context gated), with aMath.randomlast resort.turnIdwithz.string().uuid().compose.https.yaml: opt-in TLS overlay for LAN deploymentsAGENTCONNECT_HTTPS_HOSTvariable.PUBLIC_*URLs, CORS origin, and setup-server passthroughs to the HTTPS origins. Daemons are Node processes and keep dialing the plain HTTP/WS ports unchanged.compose.env.example, and the agentconnect-setup skill document the flow, including trusting the exported root certificate per device.Verification
random-id.test.ts),tsc --noEmit, eslint, prettier, and a productionnext buildall clean.docker compose configresolves the merged file with every URL override in place and fails with an actionable message whenAGENTCONNECT_HTTPS_HOSTis unset; the Caddyfile passescaddy validate.🤖 Generated with Claude Code