Services and ports
HWT gives every configured service a stable, collision-free local port for each linked worktree.
ports: start: 20000 end: 39999 services: [web, assets]A repository ports.services list may place <global> where globally configured
services should be inserted. The default inclusive range is 20000 to 39999.
Localhost URLs
Section titled “Localhost URLs”Each service receives a direct URL that works without system configuration:
HWT_PORT_WEB="20000"HWT_URL_WEB="http://feature-login.web.localhost:20000"HWT_PORT_ASSETS="20001"HWT_URL_ASSETS="http://feature-login.assets.localhost:20001"RFC 6761 reserves localhost names for loopback resolution. These URLs require
no sudo, daemon, dnsmasq, Caddy, or hosts-file entry. HWT writes them to the
worktree environment. A named
worktree URL can expose the same complete value.
Customize direct URLs with ports.url_template. It supports normalized
{worktree} and {service} labels, the generic {hostname}, and {port}:
ports: services: [web] url_template: http://{service}.{hostname}:{port}For an existing wildcard domain where ports distinguish worktrees:
ports: services: [web] url_template: https://app.acme.dev:{port}HWT only generates the URL. The service must provide HTTPS for that hostname, or a separately configured proxy must listen on the allocated port and terminate TLS.
Allocation
Section titled “Allocation”HWT stores allocations in
${XDG_STATE_HOME:-~/.local/state}/hwt/ports.json. Allocation and cleanup hold
an advisory process lock so concurrent HWT processes do not select the same port.
Each service receives the first free port in the configured range, after HWT
checks that the port is not already in use.
The registry contains only canonical worktree paths and integer ports. It does not contain environment values or credentials. Exact ports depend on active reservations and other listeners, not service order alone.
Reservations prevent conflicts among HWT worktrees but cannot hold an operating system socket until a future server starts. If another process claims a recorded port, stop the affected development services and run:
hwt env --refreshRefresh replaces all ports for the current worktree. hwt remove releases its
allocation. Before a new allocation, HWT also removes entries whose worktree path
no longer exists.
Optional managed DNS
Section titled “Optional managed DNS”Managed local DNS replaces direct localhost URLs with portless service URLs:
ports: services: [web, assets]local_dns: enabled: true domain: hwt.testHWT derives a stable base hostname from the canonical repository and worktree
paths. A result resembles app-feature-login-a1b2c3d4e5f6.hwt.test. The path
hash prevents repositories or normalized names from silently sharing a hostname.
Each service is routed at <service>.<base-hostname> through Caddy to its
allocated port.
hwt dns setup creates these HWT-owned files under
${XDG_STATE_HOME:-~/.local/state}/hwt/local-dns/:
dnsmasq.confcontains wildcard loopback mappings for configured local domains.Caddyfilecontains exact service hosts and their127.0.0.1:<port>upstreams.registry.jsoncontains canonical paths, hostnames, and service ports.
HWT does not install packages, edit /etc, invoke sudo, start listeners, or
manage host services. Include the reported snippets from separately installed
dnsmasq and Caddy services. Managed DNS is supported on macOS and Linux.
| Command | Purpose |
|---|---|
hwt dns setup [--json] |
Generate snippets and report their include paths. |
hwt dns status [--json] |
Inspect generated paths and active registrations. |
hwt dns refresh [--cwd, --json] |
Reconcile the current worktree route without replacing its ports. |
hwt dns teardown [--force, --json] |
Remove HWT-owned state; refuse active routes unless forced. |
An optional local_dns.reload argv command can reload user-managed services
after generated files change. HWT runs it directly without a shell. Arguments
may contain {caddyfile}, {dnsmasq}, and {state_dir}. Failed reloads restore
the previous registry and generated files.
Delete worktrees through hwt remove so routes are cleaned up. A checkout
deleted by another tool keeps its route because HWT cannot confirm successful
cleanup; inspect it with hwt dns status.