Skip to content

fix(dev): serve each dev server at <worktree>.localhost - #51

Merged
asfires merged 1 commit into
mainfrom
t3code/dev-localhost-subdomains
Aug 19, 2026
Merged

fix(dev): serve each dev server at <worktree>.localhost#51
asfires merged 1 commit into
mainfrom
t3code/dev-localhost-subdomains

Conversation

@asfires

@asfires asfires commented Aug 18, 2026

Copy link
Copy Markdown
Owner

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 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 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 *.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 by default.

  • One knob: the default VITE_DEV_SERVER_URL in scripts/dev-runner.ts. The startup pairingUrl, t3 pair, the CORS dev origin, and the backend→web redirect all follow from ServerConfig.devUrl. The [dev-runner] line now prints the origin.
  • Loopback predicates accept the .localhost suffix: server http.ts (dev redirect), startupAccess.ts, packages/shared/preview.ts (used by t3 pair --tailscale so the Serve target stays loopback), web target.ts (dev-origin rewrite), hostFonts.ts. Each is a one-line change with tests.
  • fix(server): sweep dead dev session cookies at pairing #47 reverted: apps/server/src/auth/ is byte-identical to upstream again; docs note replaced; one clause in AGENTS.md.
  • Unchanged: desktop (127.0.0.1), --share (tailnet URL still overrides), hosted/daily-driver servers (plain host, stable t3_session), mobile, T3 Connect. No header-size bump.

Verification

  • vp test run on 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 on main — pre-existing.)
  • Booted this worktree's dev server: [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, and t3 pair prints the same origin.

Transition: the one-time localhost cookie 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).

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).
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for cf870c0.

This comment will update automatically after the next completed run.

@asfires
asfires merged commit 1fc5b49 into main Aug 19, 2026
6 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant