Named URLs and addressing
$ wtm run url feat/x --job shop-apihttp://api.feat-x.myrepo.localhost:11080$ wtm run url feat/x --job shop-api --rawhttp://localhost:4011$ curl "$(wtm run url feat/x --job shop-api)/health"Two ways to reach a job
Section titled “Two ways to reach a job”- A port URL is the job's own port:
http://localhost:4012. Every worktree binds its own port, so two worktrees never collide, but they sharelocalhost, and with it the browser's cookie jar and every CORS origin. - A named URL is served by the run proxy, which lives in the run daemon:
http://api.feat-x.myrepo.localhost:11080. Each worktree gets its own hostname, so two of them stop sharing a cookie or an origin.*.localhostnames the loopback in browsers and most HTTP clients, so nothing is added to/etc/hosts.
A job opts into a name with a url table naming the declared port that speaks HTTP:
[[job]]name = "shop-api"kind = "service"cmd = "pnpm dev" [job.ports] PORT = 4001 [job.url] port = "PORT" # the declared port the proxy forwards to host = "api" # optional: the first label, the job's name when absentThe host is <host>.<worktree>.<repo>.localhost: <worktree> is the branch as a DNS-safe slug, <repo> the repository's directory name. A shared service has one address for the whole repository, so its host carries no worktree segment. wtm run init offers a name to every service that declares the port it listens on; wtm run job add|edit --url-port PORT --url-host api set it by hand.
wtm run url [worktree] --job <name> prints a job's named URL (--raw the port URL) and nothing else, for $(…); wtm run open hands it to the browser. run up, run ps and the run view show the same addresses.
The proxy's port
Section titled “The proxy's port”The proxy listens on the loopback only, on port 11080 by default, set in the global config:
[proxy]port = 11080 # 0 or absent means this defaultenabled = true # false: every surface hands out port URLs insteadA port it cannot bind costs the names, never the jobs. wtm run proxy status reports the configured and real ports and whether port 80 is redirected.
On macOS, wtm run proxy install removes the port from every named URL: it installs a per-user LaunchAgent: launchd binds port 80 on the loopback and hands the socket to wtm, which relays it to the proxy. No sudo, no system file; wtm run proxy uninstall removes it. On other systems the named URLs keep their :11080.
Addressing: what a .env value holds
Section titled “Addressing: what a .env value holds”When a .env value points at another job (VITE_API_URL, CORS_ORIGIN), wtm rewrites it for each worktree through an [[env_port]] link. addressing in run.toml decides what that rewrite writes:
"names"(the default when the key is absent) writes the job's whole named origin,http://api.feat-x.myrepo.localhost, whenever the linked job publishes a URL for that port and the value has the shape of a URL. A barePORT=4011stays a number, and aDATABASE_URLstays a port: the proxy only speaks HTTP."ports"writes port numbers everywhere.
The choice has a consequence outside wtm: named URLs answer while wtm run runs the job, and not when you start the app yourself. A project whose author launches dev servers by hand wants "ports". On a machine where the proxy is off, ports are written whatever the mode says, and a notice says so. Under "ports" the run surfaces also hand out port URLs and register no name.
wtm run addressing ports --yes # port numbers, every worktree's .env settledwtm run addressing names --yes # back to named originswtm run addressing names --yes --keep-env # switch run.toml aloneThe switch writes addressing in run.toml, then settles the worktrees whose .env spells the other mode (an interactive run asks first; wtm run addressing alone asks for the mode). Under "names" the named URL is a worktree's only working entrance: opening its port directly sends an Origin the API no longer knows.
The main checkout
Section titled “The main checkout”The main checkout is the one that exists without wtm, the one a colleague clones and a docker compose up reads, so nothing moves it onto names unless you ask: a switch brings it back to ports but never onto names, and wtm env main keeps whatever its .env spells. You choose, when you want it:
wtm env main # the wizard asks: keep ports, or move onto nameswtm env main --addressing names --yes # move main onto the named URLswtm env main --addressing ports --yes # bring main back to ports, and only main- Until then, under
"names", main's working entrance is its port URL. wtm still hands out its name everywhere (run up,run url,run open, the run view,wtm ui) and adds a line saying the.envstill spells ports;--rawgives the port URL. The route is registered either way, so nothing has to restart. - Afterwards, whoever reads main's
.envor starts its stack depends on the proxy being up. It is worth it right after switching a project to"names", and afterwtm run proxy install, which drops the:11080from the origins already written. - Reconciling main's keys (
wtm env main,--yes,--check) never changes its addressing: it is read against the mode its.envalready spells. It writes no[[env]]namespace into main either, so--addressing portsreally does give back a checkout without wtm; see Shared services. --addressingis the main checkout's alone: a linked worktree followsrun.toml(wtm run addressing), and--addressing namesis refused while the project addresses by ports.wtm createfrom main is unaffected either way: a copied value carrying main's segment is rewound to the new worktree's.