fix(dev): serve each dev server at <worktree>.localhost - #51
Merged
Conversation
Cookies are scoped by host, not port. Every worktree dev server was served at localhost:<port> and set a 30-day session cookie on host "localhost", so the browser sent all of them to every loopback server on the machine, including the daily driver on :3773. Each is ~650 bytes; at ~25 dev pairings Node's 16KB header cap turned every loopback request into a silent 431. PR #47 tried to manage the shared jar with name-encoded sweeps; that only ran when a dev server paired and could not run at all once over the cap. Dev servers now default their web origin to http://<slug>.localhost:<port>, slug from the worktree (or cwd) basename. Browsers resolve *.localhost to loopback with no DNS, treat it as a secure context, and keep one cookie jar per host, so pileup is impossible by construction. Vite already allows *.localhost. The one knob is VITE_DEV_SERVER_URL in dev-runner; the startup pairing URL, t3 pair, CORS origin, and the backend redirect follow from it. Loopback predicates in server http, startupAccess, shared preview, web target, and hostFonts accept the suffix. #47 is reverted; auth/ is upstream-identical again. Desktop, --share, hosted and daily-driver servers are unchanged. Verified: worktree dev server boots with pairingUrl http://t3code-dev-localhost-subdomains.localhost:8422/pair#..., Vite answers on that host, the backend redirects to it, t3 pair prints it. Model: GPT-5.6 Sol via Codex CLI (implementation) from a spec by Claude Fable 5 via Claude Code (design, review, verification).
Thread transfer impact
This comment will update automatically after the next completed run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Cookies are scoped by host, not port. Every worktree dev server was served at
localhost:<port>and set a 30-day session cookie on hostlocalhost, so the browser sent all of them to every loopback server on the machine — including the daily driver on :3773. Each is ~650 bytes; at ~25 dev pairings Node's 16 KB header cap turned every loopback request into a silent 431 (nothing reaches the app, nothing logs). Fork PR #47 tried to manage the shared jar with name-encoded sweeps; that only ran when a dev server paired and could not run at all once over the cap.Fix
Stop sharing the jar. Dev servers now default their web origin to
http://<slug>.localhost:<port>(slug = sanitized worktree or cwd basename). Chrome and Firefox resolve*.localhostto loopback with no DNS, treat it as a secure context, and keep one cookie jar per host, so pileup is impossible by construction. Vite already allows*.localhostby default.VITE_DEV_SERVER_URLinscripts/dev-runner.ts. The startuppairingUrl,t3 pair, the CORS dev origin, and the backend→web redirect all follow fromServerConfig.devUrl. The[dev-runner]line now prints the origin..localhostsuffix: serverhttp.ts(dev redirect),startupAccess.ts,packages/shared/preview.ts(used byt3 pair --tailscaleso the Serve target stays loopback), webtarget.ts(dev-origin rewrite),hostFonts.ts. Each is a one-line change with tests.apps/server/src/auth/is byte-identical to upstream again; docs note replaced; one clause in AGENTS.md.--share(tailnet URL still overrides), hosted/daily-driver servers (plain host, stablet3_session), mobile, T3 Connect. No header-size bump.Verification
vp test runon dev-runner, shared preview, server http/pair/auth/startupAccess, web target/hostFonts: 198 tests green. Server and web typecheck clean; targeted lint/fmt clean. (server.test.ts's transfer-budget benchmark fails identically onmain— pre-existing.)[dev-runner] ... devUrl=http://t3code-dev-localhost-subdomains.localhost:8422,pairingUrl: http://t3code-dev-localhost-subdomains.localhost:8422/pair#token=...; Vite answers 200 on that host (both via Host header and natural resolution), the backend port 302s to it, andt3 pairprints the same origin.Transition: the one-time
localhostcookie clear already drained the jar; no new dev cookies land there again and any stragglers age out in ≤30 days.Implementation by GPT-5.6 Sol via Codex CLI from a spec by Claude Fable 5 via Claude Code (design, review, verification).