Skip to content

fix(proxy): route wildcard subdomains to the closest registered parent - #4

Merged
rqbazan merged 1 commit into
mainfrom
fix/wildcard-most-specific-host
Sep 15, 2026
Merged

rqbazan merged 1 commit into
mainfrom
fix/wildcard-most-specific-host

Conversation

@rqbazan

@rqbazan rqbazan commented Sep 15, 2026

Copy link
Copy Markdown
Member

Summary

With --wildcard, findRoute chose among every registered parent of the request hostname by path length only. That caused two defects:

  • Order dependence (inherited from upstream, vercel-labs/portless#380): between nested parents with the same path, the first one in routes.json won.
  • Path beats host (introduced by --path): a longer prefix on a farther parent beat the root route of a closer parent, whatever the order.

Example: a plain checkout on acme.localhost and a worktree on feat-x.acme.localhost. A request to tenant.feat-x.acme.localhost could be served by the plain checkout, and restarting a single app of the worktree mixed both stacks on one page.

Change

The closest parent now owns the request, the same way an exact match does. The closest parent is the longest matching hostname, which is always a dot-bounded suffix of the request. Path selection only looks at that hostname's routes. If it has no route for the path, the proxy returns 404 instead of falling through to a farther parent.

Docs updated: README (subdomains and path-based routing sections), skills/portless/SKILL.md, NOTICE, docs/SYNCING.md.

Verification

  • New tests in proxy.test.ts, all failing before the fix:
    • the most specific parent wins in both route orders
    • a closer parent's root beats a farther parent's /settings
    • a request returns 404 when the closest parent has no route for the path
  • pnpm format:check && pnpm lint && pnpm type-check && pnpm build && pnpm test && pnpm test:e2e: all green (982 unit, 16 e2e).
  • Isolated proxy run: plain HTTP on :18080, its own state dir, no hosts sync. Aliases: acme -> main, acme --path /settings -> main-settings, feat-x.acme -> feat-x. Registered in both orders:
Request 0.0.2, order A 0.0.2, order B this branch, both orders
tenant.feat-x.acme.localhost/ main feat-x feat-x
tenant.feat-x.acme.localhost/settings/ main-settings main-settings feat-x
tenant.acme.localhost/ main main main
tenant.acme.localhost/settings/ main-settings main-settings main-settings

No version bump here. The 0.0.3 release follows in its own PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LmsBY2Z3tFzHVrRHiVTfmZ

The wildcard tier of findRoute chose among every registered parent of the
request hostname by path length only. The result depended on routes.json
order (upstream vercel-labs#380), and a longer path prefix on a
farther parent beat the root route of a closer one. With a plain checkout on
acme.localhost and a worktree on feat-x.acme.localhost,
tenant.feat-x.acme.localhost could be served by the plain checkout, and
restarting a single app of the worktree mixed both stacks on one page.

The closest parent now owns the request like an exact match does: path
selection only looks at that hostname's routes, and a path it does not serve
is a 404 instead of falling through to a farther parent.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01LmsBY2Z3tFzHVrRHiVTfmZ
@rqbazan
rqbazan force-pushed the fix/wildcard-most-specific-host branch from 35f2eab to c87709b Compare September 15, 2026 12:29
@rqbazan
rqbazan merged commit 326f4c2 into main Sep 15, 2026
2 checks passed
@rqbazan
rqbazan deleted the fix/wildcard-most-specific-host branch September 15, 2026 12:34
@rqbazan rqbazan mentioned this pull request Sep 15, 2026
rqbazan added a commit that referenced this pull request Sep 15, 2026
Bump to 0.0.3 and add the changelog entry. The release ships the wildcard
routing fix from #4: a subdomain goes to the closest registered parent
hostname, and path selection no longer falls through to a farther parent.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01LmsBY2Z3tFzHVrRHiVTfmZ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant