# LAN の HTTP アクセスで `crypto.randomUUID()` が WebSocket 接続開始を阻害する ## 概要 Workspace WebUI の Vite dev server を `--host` 付きで LAN に公開し、別端末から `http://192.168.1.32:5173` にアクセスすると、Worker Console の protocol 状態が `connecting` のまま進まなかった。 原因は WebSocket や Vite proxy ではなく、WebSocket multiplexing の相関 ID 生成に `crypto.randomUUID()` を直接使っていることである。`http://localhost` はブラウザから potentially trustworthy origin として扱われる一方、LAN IP 上の平文 HTTP は secure context ではない。`crypto.randomUUID()` は secure context 限定なので、LAN アクセスでは 接続処理が WebSocket の生成前に例外終了する。 ## 発生経路 Worker Console の `connectProtocolTransport` は、最初に `protocolState` を `"connecting"` に設定してから `WorkspaceMultiplexer.subscribe()` を呼ぶ。 - `web/workspace/src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte` - `protocolState = "connecting"` - 直後に `workspaceMultiplexer(...).subscribe(...)` - `web/workspace/src/lib/workspace/multiplexer.ts` - `subscribe()` の先頭で `const clientId = crypto.randomUUID()` - その後の `#ensureConnected()` で初めて `new WebSocket(url)` を実行する このため `crypto.randomUUID()` が利用できないブラウザでは、画面状態だけが `connecting` に更新された後に同期例外が発生し、WebSocket request 自体が送信されない。 接続失敗を示す `closed` や diagnostic にも遷移しないため、見た目からは WebSocket の 接続待ちに見える。 同じ multiplexer では `crypto.randomUUID()` を次の3用途に使っている。 1. ブラウザ内で subscription を区別する `clientId` 2. `subscribe_events` request と response を対応付ける `request_id` 3. `unsubscribe_events` の `request_id` いずれも暗号化や認証のためではなく、ローカル Map のキーまたはプロトコル上の相関 ID である。secure-context-only API を要求する必要性はない。 ## 切り分け結果 開発ホスト上から次の条件で `/api/w//protocol/ws` に WebSocket upgrade を 送ると、backend への直接接続と Vite の `5173` proxy 経由の両方で `101 Switching Protocols` が返った。 - `Host: 192.168.1.32:5173` - `Origin: http://192.168.1.32:5173` したがって、現在の Vite 設定にある `proxy["/api"].ws = true` と、loopback に bind した backend への proxy はこの現象の直接原因ではない。 ## 改善案 - `WorkspaceMultiplexer` の client/request correlation ID は、module-local の単調増加 sequence から生成する。これらは1ページ・1接続内で重複しなければよく、暗号学的乱数や 永続的なglobal uniquenessは不要なので、`crypto`、`Math.random()`、時刻依存のfallbackを 使わない。 - `WorkspaceMultiplexer.subscribe()` の同期初期化失敗を Console の diagnostic/state に 反映し、初期値の `connecting` に留まらないようにする。 - LAN 上の平文 HTTP を開発時の対応経路とするなら、insecure context から subscription 初期化できることを regression test として固定する。 - 本番相当の LAN 公開では HTTPS を使う。Passkey/WebAuthn も secure context を要求し、 現在の開発設定は `rp_id = localhost` なので、LAN origin を正式対応する場合は auth の origin/RP ID 設計も別途必要になる。 ## 補足 `--host` は Vite の listener を LAN に bind するだけであり、配信 origin を secure context に変えるものではない。localhost で正常に動くことだけでは、LAN IP の HTTP アクセスでも同じブラウザ API が利用できることを証明できない。 ## Resolution (2026-08-06) `WorkspaceMultiplexer` の client/request ID をmodule-localの単調増加sequenceへ変更した。 これによりWebSocket接続開始経路からsecure-context限定の `crypto.randomUUID()` 依存を除去した。 IDはlocal correlationにのみ使われ、認証・認可・capability authorityには使われない。