Networking
Integrations break when ports change between worktrees. Git Treeline gives you four networking commands — local HTTPS names, a port facade for services locked to localhost:3000, a public route on your domain, and a disposable link — so you pick the one that fits without juggling unrelated tools.
You register localhost:3000 as your OAuth callback. You switch to a worktree on port 3010. Login breaks.
OAuth callback received at localhost:3000
ERR: No server listening on port 3000
# Redirect URI was registered as localhost:3000.
# This worktree runs on :3010.
# Change the URI? Break the other worktree.
Stripe webhooks, Mapbox keys, CI callbacks, SSO redirects · all hardcoded to a port or hostname that keeps changing between worktrees.
The URL follows the branch, not the port
localhost:3010 Ephemeral port
https://myapp-feature-auth.prt.dev HTTPS URL for this branch
localhost:3010 still works everywhere — HTTP always reaches your worktree. The router gives every branch a named HTTPS URL like https://myapp-feature-auth.prt.dev so you open a branch name instead of remembering a port number. Need a fixed origin? gtl proxy 3000 keeps localhost:3000 as a stable facade.
Local HTTPS naming, sharing a URL, and being reachable from the public internet are different jobs. Git Treeline maps each to a command so you are not duct-taping ports by hand.
Each branch gets a predictable hostname like myapp-feature-auth.prt.dev with HTTPS from a trusted local CA. For services locked to a specific port, gtl proxy 3000 keeps localhost:3000 as a stable facade that routes to whichever worktree you’re in — configure OAuth, Stripe, and Maps once.
Use gtl tunnel when you need a stable hostname on a domain you control—OAuth providers and webhooks see a URL that does not change when you switch machines. Use gtl share for a short-lived, token-gated URL, or --tailscale when the audience is only on your tailnet. Teardown is explicit when you are done.
A tunnel gives your local worktree a public route so payment processors, CI callbacks, and mobile deep links can hit the branch you have running—not a stale staging box. Same mental model as local HTTPS, with a different trust boundary.
Each command maps to a different network boundary. serve names branches locally. proxy pins a listen port. tunnel publishes a stable route on your domain. share issues a disposable URL. Mix as needed.
Local HTTPS subdomain router
A background router that maps {project}-{branch}.prt.dev to the right worktree port. HTTPS with a trusted local CA. Runs as a system service · install once per machine. Safari on macOS: gtl serve hosts sync. Extra subdomains for Redis UIs and the like: gtl serve alias. See networking docs.
Best for: OAuth redirects, API keys tied to a hostname, day-to-day development where you want named URLs instead of port numbers.
$ gtl serve install
Generated trusted CA certificate
Installed system service
Listening on https://*.prt.dev
Port facade
Treeline never allocates common dev ports like 3000, 4000, 5000, or 8080 — they stay free on purpose. gtl proxy 3000 claims that port and routes traffic to whichever worktree you’re in. OAuth callbacks, Stripe webhooks, Mapbox origin checks, and CORS policies all stay registered to localhost:3000. You configure them once and never touch a dashboard again.
Run from a worktree directory and it infers the target port, or specify both explicitly. Optional TLS (requires a local cert tool like mkcert).
Best for: Any third-party service hardcoded to a specific localhost port — OAuth providers, webhook endpoints, mapping APIs, CORS allowlists. Full use case →
$ gtl proxy 3000
Proxying :3000 → :3050
Public HTTPS on your domain
Exposes the worktree at {route-key}.your-domain via Cloudflare. Same route key as gtl serve. Multiple named tunnel configs are supported; set a default with gtl tunnel default <name> and pass --tunnel <name> to override. Run it from multiple worktrees at once on the same named tunnel and they multiplex through a single cloudflared with merged ingress.
Best for: OAuth redirect URIs, webhooks, and any integration that needs a predictable public hostname.
$ gtl tunnel
Tunnel active
https://myapp-feature-auth.example.dev
Ephemeral access
Token-gated reverse proxy over a tunnel: recipient opens the link, gets a session cookie, then sees clean URLs. New token and hostname each run; everything stops on Ctrl+C. With a named tunnel you get your domain; otherwise quick tunnels use *.trycloudflare.com. gtl share --tailscale uses Tailscale Serve for tailnet-only access, no token flow.
Best for: “Send me the link” demos, not for registering long-lived redirect URIs. Share URLs are not guessable from branch names.
$ gtl share
Share URL (token required)
https://…
Ctrl+C tears down tunnel and proxy
| Goal | Command |
|---|---|
| Stable local hostname per branch | gtl serve |
Integration locked to localhost:3000 | gtl proxy |
| Public URL on your domain for webhooks / OAuth | gtl tunnel |
| Temporary link; teardown when done | gtl share |
Most teams only need gtl serve. gtl serve install sets up a local CA, port forwarding, and a background service (requires sudo twice on macOS/Linux). After that, every branch gets a named HTTPS URL.
gtl serve install