portless
Portless Integration
Named .localhost URLs for local development. Replaces localhost:3000 with https://myapp.localhost.
Full CLI reference: Load
Read("${CLAUDE_SKILL_DIR}/references/upstream.md")for complete command docs.
New in 2026-04 (portless 0.10.x)
- HTTPS on 443 by default (breaking from 0.9.x http:1355). Valid cert, no setup.
--no-tlsreverts. NODE_EXTRA_CA_CERTSauto-injected (0.10.2) into child processes — node HTTPS calls trust portless CA with zero setup.--wildcardsubdomains —https://*.myapp.localhostfor multi-tenant / preview routing.portless alias <name> <port>— map a docker-compose / emulate port to a named URL without a long-runningrunprocess.portless clean— full teardown: stops proxy, removes CA, wipes state, cleans/etc/hosts.--lanmode — mDNS.localhostnames reachable across wifi (phone, tablet, other machines) without router config.- Fixed app ports —
--app-port 3000/PORTLESS_APP_PORTfor tools that need a known port (debuggers, docker). - hosts-sync on by default for Safari compat (disable with
PORTLESS_SYNC_HOSTS=0). - HTTP/2 HMR fixes for Vite/VitePlus/Next.js dev — websocket upgrades no longer break under h2.
- Expo / React Native support —
portless run expo startgives Metro a stable URL for device QR codes.
When to Use
- Starting a dev server that agents or browser tests will target
- Running multiple services locally (API + frontend + docs)
- Working in git worktrees (branch-named subdomains)
- Local OAuth flows (stable callback URLs)
- Connecting emulate API mocks to named URLs
Quick Start
# Instead of: npm run dev (random port)
portless run npm run dev
# → https://myapp.localhost (stable, named, HTTPS on 443 — default in 0.10+)
# Multi-service
portless run --name api npm run dev:api
portless run --name web npm run dev:web
# → https://api.localhost, https://web.localhost
# LAN mode (0.10.0) — reachable from phone/tablet via mDNS
portless proxy start --lan
portless run npm run dev
# → https://myapp.local (resolves across the local network, no router config)
# Full teardown (0.10.1) — stops proxy, removes CA, wipes state, cleans /etc/hosts
portless clean
0.10.x breaking change: default switched from
http://app.localhost:1355tohttps://app.localhoston port 443. Use--no-tlsto revert.NODE_EXTRA_CA_CERTSis injected into child processes automatically (0.10.2) — no manual cert setup./etc/hostsis synced automatically for Safari; disable withPORTLESS_SYNC_HOSTS=0.
Framework-Specific Setup
Load
Read("${CLAUDE_SKILL_DIR}/references/framework-integration.md")for full framework recipes.
Most frameworks (Next.js, Vite, Express) work with portless run <cmd>. Some need explicit flags:
| Framework | Auto-detected? | Extra flags needed |
|---|---|---|
| Next.js | Yes | None |
| Vite / Astro | Yes | None |
| Express / Fastify / Hono | Yes | None (reads PORT env var) |
| Ruby on Rails | Yes | None |
| FastAPI / uvicorn | No | --port $PORT --host $HOST |
| Django | No | $HOST:$PORT positional arg |
Why .localhost?
| Feature | .localhost (RFC 6761) |
127.0.0.1:PORT |
/etc/hosts hack |
|---|---|---|---|
No /etc/hosts editing |
Yes | Yes | No |
| HTTPS with valid cert | Yes | No | Manual |
| Wildcard subdomains | Yes | No | No |
| Works in all browsers | Yes | Yes | Varies |
| Cookie isolation per service | Yes | No | Yes |
| No port conflicts | Yes | No | Yes |
Key Environment Variables
When portless runs your command, it injects:
| Variable | Value | Use in agents |
|---|---|---|
PORT |
Assigned ephemeral port (4000-4999) | Internal only |
HOST |
127.0.0.1 |
Internal only |
PORTLESS_URL |
https://myapp.localhost |
Use this in agent prompts |
NODE_EXTRA_CA_CERTS |
Path to portless CA (auto-injected 0.10.2) | Child node processes trust portless certs without setup |
Toggle env vars
| Variable | Effect |
|---|---|
PORTLESS=0 |
Bypass portless entirely (CI) |
PORTLESS_SYNC_HOSTS=0 |
Disable auto /etc/hosts sync (default: on in 0.10.1+) |
PORTLESS_STATE_DIR |
Override state dir (default: ~/.portless or /tmp/portless for privileged ports) |
OrchestKit Integration Patterns
1. Agent-Accessible Dev Server
# Start with portless, then agents can target PORTLESS_URL
portless run npm run dev
# In ork:expect or agent-browser:
agent-browser open $PORTLESS_URL
2. Emulate + Portless (Named API Mocks)
# Register emulate ports as named aliases
portless alias github-api 4001
portless alias vercel-api 4000
portless alias google-api 4002
# Now agents can target:
# https://github-api.localhost — GitHub emulator
# https://vercel-api.localhost — Vercel emulator
3. Git Worktree Dev
# In worktree for feature/auth-flow:
portless run npm run dev
# → https://auth-flow.myapp.localhost (auto branch prefix)
4. Bypass in CI
# Disable portless in CI — direct port access
PORTLESS=0 npm run dev
Anti-Patterns
| Don't | Do Instead |
|---|---|
Hardcode localhost:3000 in tests |
Use PORTLESS_URL or process.env.PORTLESS_URL |
| Run portless in CI | Set PORTLESS=0 in CI environments |
| Use numeric ports in AGENTS.md | Document the portless URL |
References
| File | Content |
|---|---|
references/upstream.md |
Full portless CLI reference (synced from Vercel) |
references/upstream-oauth.md |
OAuth callback patterns with stable URLs |
references/framework-integration.md |
Framework recipes (FastAPI, Django, Docker, gotchas) |
checklists/new-project-setup.md |
Step-by-step: add portless to a new project |