Dev server URLs
Give each worktree's dev server a stable hostname, and tell every agent where its own one is.
Give each worktree's dev server a stable hostname, such as https://fix-ui.myapp.localhost:1355.
Without [urls], each worktree's auto: port is a different number that a person or agent has to
look up. With it, boxer registers a name for each forwarded port with
portless, a local reverse proxy that routes by hostname.
Turn on URLs
Install portless:
npm install -g portlessTurn on [urls] in boxer.toml:
start = ["npx next dev --webpack -p 3000 -H 0.0.0.0"]
[network]
ports = ["auto:3000"]
[urls]
enabled = true$ boxer up
boxer: url: https://fix-ui.myapp.localhost:1355 -> guest port 3000The server must listen on 0.0.0.0 inside the guest. See
Forward ports.
Without portless installed, boxer up prints a warning and everything else works. Ports are still
forwarded, and boxer url prints http://127.0.0.1:<host port>.
Hostnames
| worktree | URL |
|---|---|
| the main checkout | https://myapp.localhost:1355 |
a linked worktree on branch fix-ui | https://fix-ui.myapp.localhost:1355 |
| a detached worktree, or one whose name is taken | https://swift-crab.myapp.localhost:1355 |
myappis the repository's directory name. Seturls.nameto change it.- portless supplies the branch prefix.
- portless gives a detached worktree no prefix. Most orchestrators create detached worktrees.
- portless gives two branches ending in the same segment the same prefix, such as
feat/loginandfix/login. - In those two cases boxer uses the sandbox's two-word name, such as
swift-crab. - boxer never takes a name another sandbox or a person registered.
Name several ports
With several ports, each is named <label>.<repo>, after the branch prefix. The label comes from
urls.names, a devcontainer's portsAttributes.label, or the port number:
[network]
ports = ["auto:3000", "auto:8080"]
[urls]
enabled = true
names = { "3000" = "web", "8080" = "api" } # fix-ui.web.myapp.localhost, fix-ui.api.myapp.localhostFind a URL
boxer url # the first forwarded port's address
boxer url 8080 # a specific guest port
boxer open # open it in a browser- The brief, the skill and
AGENTS.mdtell the agent to runboxer urlinstead of assuminglocalhost:<port>. - The
boxer_statusMCP tool returns each port's URL. boxer status --jsonandboxer ls --jsonlist them underurls.
A harness running inside the sandbox cannot reach the names, because the proxy
is on the host's loopback. boxer tells it the server is at 127.0.0.1:<port>. The address a person
should open is in $BOXER_URLS (3000=https://…).
Told only that a dev server is running, agents on five of eight harnesses found it by name on every model tested. See Results.
Name lifecycle
- boxer registers a name when the sandbox starts.
- boxer removes it when
down,rmorgcdeletes the sandbox. gcalso removes a route whose sandbox disappeared some other way.
If the portless proxy is not running, boxer starts it on port 1355. Set PORTLESS_PORT to 1024 or
above to use that port instead. boxer stops the proxy when the last route goes. A proxy you started
yourself is left running.
The proxy binds 127.0.0.1. See
forwarded ports and URLs.
Next.js
Next.js 15 serves the page, its chunks and hot reload through the name. It warns that a future
version will refuse cross-origin dev requests. Add *.localhost to allowedDevOrigins to cover
every worktree's name:
// next.config.ts
const nextConfig = { allowedDevOrigins: ["127.0.0.1", "localhost", "*.localhost"] };
export default nextConfig;examples/nextjs is a complete setup.