portless

Named HTTPS .localhost URLs for local development with portless (v0.10.x). Eliminates port collisions, enables stable URLs for agents, integrates with emulate for API emulation aliases, git worktrees for branch-named subdomains, and LAN mode (--lan) for mDNS .local hostnames reachable across devices. Use when setting up local dev environments, configuring agent-accessible URLs, running multi-service dev setups, or testing from phones/tablets on the same wifi. Do NOT use for production deployments, CI environments (set PORTLESS=0), or DNS/hosting configuration.

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-tls reverts.
  • NODE_EXTRA_CA_CERTS auto-injected (0.10.2) into child processes — node HTTPS calls trust portless CA with zero setup.
  • --wildcard subdomainshttps://*.myapp.localhost for multi-tenant / preview routing.
  • portless alias <name> <port> — map a docker-compose / emulate port to a named URL without a long-running run process.
  • portless clean — full teardown: stops proxy, removes CA, wipes state, cleans /etc/hosts.
  • --lan mode — mDNS .local hostnames reachable across wifi (phone, tablet, other machines) without router config.
  • Fixed app ports--app-port 3000 / PORTLESS_APP_PORT for 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 start gives 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:1355 to https://app.localhost on port 443. Use --no-tls to revert. NODE_EXTRA_CA_CERTS is injected into child processes automatically (0.10.2) — no manual cert setup. /etc/hosts is synced automatically for Safari; disable with PORTLESS_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:

FrameworkAuto-detected?Extra flags needed
Next.jsYesNone
Vite / AstroYesNone
Express / Fastify / HonoYesNone (reads PORT env var)
Ruby on RailsYesNone
FastAPI / uvicornNo--port $PORT --host $HOST
DjangoNo$HOST:$PORT positional arg

Why .localhost?

Feature.localhost (RFC 6761)127.0.0.1:PORT/etc/hosts hack
No /etc/hosts editingYesYesNo
HTTPS with valid certYesNoManual
Wildcard subdomainsYesNoNo
Works in all browsersYesYesVaries
Cookie isolation per serviceYesNoYes
No port conflictsYesNoYes

Key Environment Variables

When portless runs your command, it injects:

VariableValueUse in agents
PORTAssigned ephemeral port (4000-4999)Internal only
HOST127.0.0.1Internal only
PORTLESS_URLhttps://myapp.localhostUse this in agent prompts
NODE_EXTRA_CA_CERTSPath to portless CA (auto-injected 0.10.2)Child node processes trust portless certs without setup

Toggle env vars

VariableEffect
PORTLESS=0Bypass portless entirely (CI)
PORTLESS_SYNC_HOSTS=0Disable auto /etc/hosts sync (default: on in 0.10.1+)
PORTLESS_STATE_DIROverride 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'tDo Instead
Hardcode localhost:3000 in testsUse PORTLESS_URL or process.env.PORTLESS_URL
Run portless in CISet PORTLESS=0 in CI environments
Use numeric ports in AGENTS.mdDocument the portless URL

References

FileContent
references/upstream.mdFull portless CLI reference (synced from Vercel)
references/upstream-oauth.mdOAuth callback patterns with stable URLs
references/framework-integration.mdFramework recipes (FastAPI, Django, Docker, gotchas)
checklists/new-project-setup.mdStep-by-step: add portless to a new project