System Architecture
Wormkey is a distributed tunneling system that consists of three core components working together to expose local servers through secure public URLs:Wormkey uses a persistent WebSocket tunnel from your CLI to the Edge Gateway, eliminating the need for inbound firewall rules or port forwarding.
Component Overview
| Component | Technology | Port | Role |
|---|---|---|---|
| CLI | Node.js/TypeScript | — | Connects to Edge, forwards traffic to localhost |
| Control Plane | Node.js/Fastify | 3001 | Session creation, slug allocation, lifecycle management |
| Edge Gateway | Go | 3002 | TLS termination, routing, stream multiplexing |
Architecture Diagram
Data Flow
1. Session Creation
When you runwormkey http 3000:
- CLI → Control Plane:
POST /sessionswith port and options - Control Plane generates:
- Random slug (e.g.,
quiet-lime-82) - Session token (format:
slug.ownerToken) - Public URL (
https://quiet-lime-82.wormkey.run) - Owner claim URL (one-time use)
- Random slug (e.g.,
- Control Plane returns: Session metadata to CLI
2. Tunnel Establishment
- CLI → Edge Gateway: WebSocket connection to
/tunnel - Authorization:
Bearer slug.ownerTokenin header - Edge Gateway:
- Validates token with Control Plane
- Binds
slug→ WebSocket connection - Starts accepting public traffic for that slug
3. Request Proxying
Public request flow:- Browser sends HTTPS request to
quiet-lime-82.wormkey.run - Edge Gateway:
- TLS terminates request
- Looks up slug → tunnel connection
- Allocates stream ID
- Sends
OPEN_STREAMframe with request headers/body
- CLI:
- Receives frame
- Makes HTTP request to
localhost:3000 - Sends
RESPONSE_HEADERSandSTREAM_DATAframes back
- Edge Gateway: Forwards response to browser
All traffic is multiplexed over a single WebSocket connection using frame-based streams. Each HTTP request gets a unique stream ID.
Key Design Principles
Outbound-Only Connection
The CLI initiates and maintains the WebSocket connection to the Edge Gateway. This means:- No inbound firewall rules needed
- Works behind NAT and corporate proxies
- CLI can run on developer laptops without port forwarding
Stream Multiplexing
A single WebSocket connection carries multiple concurrent HTTP requests:- Each request gets a unique 32-bit stream ID
- Streams are independent and can overlap
- Max 100 concurrent streams per tunnel (configurable)
Stateless Edge
The Edge Gateway is designed to be stateless:- Session metadata stored in Control Plane
- Active tunnels held in memory (can be replicated)
- CLI automatically reconnects with same session token
Session Persistence
Sessions survive CLI reconnections:- CLI disconnects (network issue, sleep, etc.)
- Edge Gateway closes old connection
- CLI reconnects with same
sessionToken - Edge Gateway replaces slug → connection mapping
- Public URL remains valid
Deployment Topology
Local Development
localhost.
Production
- Control Plane: Deployed to any Node.js host (e.g., Render, Fly.io)
- Edge Gateway: Deployed to edge locations (e.g., Fly.io multi-region)
- CLI: Runs on developer machines or CI environments
The Edge Gateway needs to be accessible from public internet with wildcard TLS for
*.wormkey.run.Environment Configuration
Each component uses environment variables for service discovery:CLI
Control Plane
Edge Gateway
Security Model
Owner Authentication
- Session Token:
slug.ownerToken(e.g.,quiet-lime-82.abc123xyz) - CLI uses full token for tunnel authentication
- Owner can claim browser session via one-time URL
- Owner cookie:
wormkey_owner=ownerToken(HttpOnly)
Viewer Tracking
- Non-owner requests get a
wormkey_viewercookie with random ID - Edge Gateway tracks active viewers per session
- Owner can kick viewers or lock the wormhole
Policy Enforcement
Owner can configure:- Public/Private: Lock wormhole to owner-only
- Max Viewers: Limit concurrent viewers
- Password: Require password for viewers
- Block Paths: Block access to specific routes (e.g.,
/admin)
Next Steps
- Protocol Specification - WebSocket frame format and stream lifecycle
- Component Details - In-depth look at each component