#565
feat(run): allow repeating --name to serve one app on several hostnames
Closes #216
Summary
portless run --name can now be repeated so one dev server answers to several hostnames, for apps that pick a site from the Host header.
- Each name goes through the same label truncation and worktree prefix or
worktree.hostnameTemplateas a single--name, so{worktree}-{name}keeps every name one label deep. - Every name gets a route for each configured TLD, all pointing at the same app port.
- The first name is the primary one: it sets
PORTLESS_URL, is printed as the main->URL, and receives any Tailscale, ngrok, or Cloudflare tunnel. The others are printed asalso ->. - Identical names collapse into one. Distinct names that render to the same hostname, such as a template without
{name}, fail before the proxy or the app starts. portless <name>,portless --name, and theportless.jsonnamefield keep a single name.
Docs: run --help, portless --help, README, skills/portless/SKILL.md, and the commands, getting started, and configuration docs pages. run is already listed as omitted from the MCP server, so mcp.ts needs no change.
Example
With {"worktree":{"hostnameTemplate":"{worktree}-{name}"}} and PORTLESS_PREFIX=fix-ui:
$ portless run --name brand-a --name brand-b --name brand-c node -e 'console.log("PORTLESS_URL=" + process.env.PORTLESS_URL)'
-- fix-ui-brand-a.localhost, fix-ui-brand-b.localhost, fix-ui-brand-c.localhost (auto-resolves to 127.0.0.1)
-- Names "brand-a", "brand-b", "brand-c" (from --name flags)
-- Prefix "fix-ui" (from PORTLESS_PREFIX)
-- Using port 4653
-> http://fix-ui-brand-a.localhost:19355
also -> http://fix-ui-brand-b.localhost:19355
also -> http://fix-ui-brand-c.localhost:19355
PORTLESS_URL=http://fix-ui-brand-a.localhost:19355With a template that drops {name} ({worktree}-app):
$ portless run --name brand-a --name brand-b node -e 1
Error: Names "brand-a" and "brand-b" both resolve to "fix-ui-app.localhost". Use distinct names; if using worktree.hostnameTemplate, include {name}.Testing
pnpm type-checkandpnpm lintinpackages/portless: pass.- New
cli.test.tscases: repeated names through a{worktree}-{name}template across two TLDs (routes, shared port,PORTLESS_URL, output, duplicate collapse), the collision error with no proxy, routes, or child started, help text,PORTLESS=0passthrough, and a missing value on a repeated--name. pnpm build && vitest runinpackages/portless: 1217 passed, 1 failed. The failure iscerts.test.ts > regenerates an existing CA whose Basic Constraints extension is duplicated (#429), which fails the same way onmainlocally because OpenSSL 4.1 refuses to parse the duplicated-extension fixture.- Manual: with a real proxy,
curltofix-ui-brand-a.localhostandfix-ui-brand-b.localhostboth reached the same server with their ownHostheader.
Risks
runApp now takes a list of names. The default single-app and portless <name> paths pass a one-item list, so their behavior and output are unchanged.
@GingerAdonis is attempting to deploy a commit to the Vercel Labs Team on Vercel.
A member of the Team first needs to authorize it.