Getting Started
Expose your local development server to the internet in 30 seconds. No account required.
Prerequisites
- Node.js 20+ — check with
node --version - A local server running on any port (e.g.,
localhost:3000)
Install
Option A: Global install (recommended)
npm install -g workslocal
This gives you the workslocal command globally. Verify with:
workslocal --version
# WorksLocal v0.1.1Option B: Run without installing
npx workslocal http 3000
Uses npx to download and run WorksLocal in one step. Good for trying it out.
Create your first tunnel
Make sure your local server is running, then:
workslocal http 3000
Replace 3000with whatever port your server is on. You'll see:
──────────────────────────────────────────────────────────── ✔ Tunnel is live! Public URL: https://a8f3k2.workslocal.exposed Forwarding: http://localhost:3000 Inspector: http://localhost:4040 Subdomain: a8f3k2 Domain: workslocal.exposed Type: ephemeral Press Ctrl+C to stop the tunnel ──────────────────────────────────────────────────────────── GET / 200 62ms
That https://...workslocal.exposed URL is publicly accessible from anywhere on the internet. Share it with a teammate, paste it in a webhook dashboard, or open it on your phone.
Custom subdomain
By default, WorksLocal assigns a random subdomain. To pick your own:
workslocal http 3000 --name myapp
This gives you https://myapp.workslocal.exposed — the same URL every time you start the tunnel. Custom subdomains are persistent across restarts for authenticated users.
Subdomain rules
- Lowercase letters, numbers, and hyphens only
- 3–50 characters
- Cannot start or end with a hyphen
- Reserved names (
www,api,admin,mail, etc.) are blocked
Anonymous vs authenticated
Anonymous (no account)
Works immediately. You get:
- Random subdomain (e.g.,
a8f3k2.workslocal.exposed) - Custom subdomains with
--name(reserved for 30 minutes after disconnect) - Up to 2 simultaneous tunnels
- Anonymous tunnels expire after 2 hours
- Web inspector at
localhost:4040 - Full HTTP forwarding + catch mode
Authenticated (free account)
Sign in to get:
- Persistent subdomains — your
--namesurvives restarts permanently - Up to 5 simultaneous tunnels
- 30-day stale cleanup (unused subdomains released after 30 days)
To authenticate:
workslocal login
This opens your browser to complete the sign-in flow via GitHub OAuth (Clerk). Your token is stored locally at ~/.workslocal/config.json.
To check your auth status:
workslocal whoami
Use with different frameworks
WorksLocal works with any local server — it doesn't care what framework you're using.
Next.js
# Terminal 1 npm run dev # → localhost:3000 # Terminal 2 workslocal http 3000
Express / Fastify / Koa
# Terminal 1 node server.js # → localhost:4000 # Terminal 2 workslocal http 4000
Python (Django / Flask / FastAPI)
# Terminal 1 python manage.py runserver 8000 # Terminal 2 workslocal http 8000
Go / Rust / Java / PHP
Same pattern — start your server on a port, point WorksLocal at that port.
Stop the tunnel
Press Ctrl+C in the terminal. The tunnel closes gracefully — no orphaned subdomains.
# Or from another terminal: workslocal stop myapp # Stop all tunnels: workslocal stop --all
JSON output for scripts and AI
Every command supports --json for machine-readable output:
workslocal http 3000 --name myapp --json
{
"tunnelId": "tun_abc123",
"publicUrl": "https://myapp.workslocal.exposed",
"subdomain": "myapp",
"domain": "workslocal.exposed",
"localPort": 3000,
"inspectorUrl": "http://localhost:4040"
}This enables AI assistants (Claude, Cursor) and scripts to create and manage tunnels programmatically.
Configuration
WorksLocal stores config in ~/.workslocal/config.json:
{
"anonymousToken": "a1b2c3...",
"sessionToken": "eyJ...",
"userId": "user_xxx",
"serverUrl": "wss://api.workslocal.dev/ws"
}You typically never need to edit this file directly.
Environment variables
| Variable | Purpose | Default |
|---|---|---|
| WORKSLOCAL_SERVER_URL | Relay server WebSocket URL | wss://api.workslocal.dev/ws |
| WORKSLOCAL_API_KEY | API key for authentication | — |
How it works (30-second version)
- The CLI opens a WebSocket to the WorksLocal relay server (on Cloudflare Workers)
- The relay assigns your subdomain and creates a Durable Object for your tunnel
- When someone visits your tunnel URL, the request flows: browser → Cloudflare edge → Durable Object → WebSocket → your CLI → localhost
- Your local server responds, and the response flows back the same path
- The CLI captures every request/response in memory for the web inspector
Your traffic passes through the relay as encrypted packets. The relay is a “dumb pipe” — request and response bodies never touch server disk. All inspection and storage happens locally on your machine.
Limitations
- HTTP/HTTPS only — TCP and UDP tunneling are not supported (planned for future)
- No SSE/streaming responses yet — responses are fully buffered before sending (fix coming in next release)
- 10 MB request body limit — requests larger than 10 MB return 413
- 30-second timeout— if your local server doesn't respond within 30 seconds, the tunnel returns 504
- Single tunnel domain — only
workslocal.exposedis available (coming support for more domains) - No custom domains — bring-your-own-domain not supported yet
- In-memory request store — captured requests are lost when the CLI exits (SQLite persistence planned)
Next steps
- CLI Reference — all commands and flags
- Web Inspector — inspect requests visually
- Catch Mode — capture webhooks without a server