fix(web): default to loopback binding

This commit is contained in:
Tianyi Cui
2026-07-22 20:35:38 +08:00
parent b51d2b3d67
commit c949a52627
9 changed files with 121 additions and 23 deletions

View File

@@ -2,7 +2,7 @@
Web-shape HTTP carrier: a `node:http` server routing `/api/*` to an injected fetch-shaped handler (node:http ↔ WHATWG bridge with SSE streamed out chunk by chunk) and everything else to static file serving with the step1-locked semantics — traversal outside the dist root is 403, any miss falls back to `index.html` with HTTP 200 (SPA routing), unknown extensions ship as octet-stream, non-GET/HEAD is 405.
The package has zero workspace dependencies on purpose: the handler arrives by structural typing (`{ fetch: typeof fetch }`), so `webserver ← runtime` is a runtime injection relationship, never a package dependency. Web (browser) shape only — Electron loads dist over `file://` and carries fetch over an IPC bridge, not this server. This package never prints; the URL line belongs to the shell.
The package has zero workspace dependencies on purpose: the handler arrives by structural typing (`{ fetch: typeof fetch }`), so `webserver ← runtime` is a runtime injection relationship, never a package dependency. Callers supply both the bind `host` and `port`; `dsh web` defaults to `127.0.0.1` and accepts `--host 0.0.0.0` for deliberate network access. Web (browser) shape only — Electron loads dist over `file://` and carries fetch over an IPC bridge, not this server. This package never prints; the URL line belongs to the shell.
Client-disconnect detection hangs off the **response** `close` event, not the request: since Node 16, `IncomingMessage` `close` fires as soon as the request body is consumed (immediately for a bodyless GET), which would abort every SSE stream right after open. `RunningWebServer.close()` pairs `close()` with `closeAllConnections()` because SSE connections never end on their own.
@@ -18,6 +18,6 @@ None; this package neither assembles nor sends a provider request.
## Known Limitations and Deferred Work
- **No TLS, auth, or origin policy** — the server binds `0.0.0.0` and trusts its network; deployment hardening (or fronting it with a real reverse proxy) is deliberately out of scope for the dev-facing v1.
- **No TLS, auth, or origin policy** — callers that bind a non-loopback address expose the server to that network; deployment hardening (or fronting it with a real reverse proxy) is deliberately out of scope for the dev-facing v1.
- **The starter MIME table is minimal** — extensions beyond the vite-emitted set fall back to `application/octet-stream`; extend the table when an asset class actually ships.
- **`port` is the only listen knob** — bind address and socket options are fixed until a deployment needs them.
- **Socket options are fixed** — callers select the bind host and port, while backlog and other socket settings remain internal until a deployment needs them.