# Router.direct — Full Documentation > Router.direct exposes your localhost to the internet with secure, always-on HTTP, TCP, and UDP tunnels. A pure-Rust tunneling client with daemon persistence and self-healing reconnection — install once, run forever. This document mirrors the complete documentation available at https://router.direct/docs. It is intended for LLMs and automated tooling. --- ## Overview Router.direct is a secure tunneling service that exposes your localhost to the internet. Install a small pure-Rust CLI, point it at a local port, and get a public URL or port on the Router.direct relay — with no router configuration, no port forwarding, and no public IP required. ### How it works 1. You create a tunnel in the dashboard. The web app stores it in PostgreSQL, generates an API key (`sk_live_…`), and mirrors the key into Redis so the relay can validate it instantly. 2. The CLI dials the relay over a TLS-encrypted control connection to `77.42.121.237:2333` and authenticates with your API key. 3. For each incoming public connection the relay tells the CLI to open a second TLS data channel, then pipes bytes in both directions to your local service. ``` You (public visitor) Your machine (behind NAT) │ │ │ https://myapp.router.direct │ ▼ │ ┌─────────────┐ ┌──────────┐ ┌──────────────────────┐ │ Nginx │─────▶│ Relay │────▶│ router CLI (TLS) │──▶ 127.0.0.1:3000 │ edge :443 │ 8080 │ :2333 │ ctrl│ control + data ch. │ └─────────────┘ └──────────┘ └──────────────────────┘ ``` ### Tunnel types - **HTTP** — a public `https://yourname.router.direct` URL routed by the Host header to a local web server. - **TCP** — a dedicated public port on the relay (10000–20000 range) for SSH, databases, VNC, or any raw TCP service. - **UDP** — a dedicated public UDP port for WireGuard, game servers, DNS, or any datagram service. ### What makes it different - **Daemon persistence** — `router install` registers a native service (systemd, OpenRC, launchd, or a Windows scheduled task) so your tunnel survives reboots and logouts. - **Self-healing reconnection** — dropped connections are retried with exponential backoff (capped at 60s), with a 40-second watchdog and 30-second heartbeats to detect dead links fast. - **Zero maintenance** — the CLI checks for updates on startup and can self-update with `router update`. - **Instant revocation** — deleting a tunnel removes its key from Redis immediately; the relay rejects it on the next connection attempt. - **Pure Rust** — a single static binary per platform, memory-safe, no runtime dependencies. --- ## Quickstart From zero to a public URL in about five minutes. Assumes a local web app on port 3000. ### 1. Create an account Sign up at https://router.direct/signup with an email and password (8+ characters). ### 2. Create a tunnel 1. Open the dashboard at https://dashboard.router.direct. 2. Click **Create Tunnel** and choose a type: - **HTTP** — pick a subdomain, e.g. `myapp` (lowercase letters, numbers, and hyphens only). - **TCP** — a public port in the 10000–20000 range is assigned automatically. 3. Copy the generated API key — it looks like `sk_live_…`. ### 3. Install the CLI macOS & Linux: ``` curl -fsSL https://router.direct/install.sh | sh ``` Windows (PowerShell): ``` iwr https://router.direct/install.ps1 -UseBasicParsing | iex ``` Verify: `router --version` ### 4. Start your tunnel (foreground) ``` router run \ --server 77.42.121.237:2333 \ --token sk_live_yourkey \ --subdomain myapp \ --local 127.0.0.1:3000 ``` Expected output: ``` 🔵 Connecting to relay 77.42.121.237:2333 (TLS)... ✅ Authenticated! 🌐 Public URL: https://myapp.router.direct 🏠 Forwarding: 127.0.0.1:3000 (TCP) 🔄 Awaiting incoming connections... ``` Open https://myapp.router.direct in a browser — you're live. **Important:** `--local` must be a full `host:port` address. Passing just a port (e.g. `--local 3000`) will fail to connect. ### 5. Make it permanent ``` router install \ --server 77.42.121.237:2333 \ --token sk_live_yourkey \ --subdomain myapp \ --local 127.0.0.1:3000 ``` - **Linux**: systemd unit (system-wide with root, user unit otherwise; OpenRC on Alpine). - **macOS**: launchd agent with KeepAlive. - **Windows**: scheduled task that restarts on logon. ### 6. Manage the service ``` router status # check health journalctl --unit router-direct --follow # logs (Linux, system service) router uninstall # remove the service ``` The default service name is `router-direct`; pass `--service-name myapp` to `install`, `status`, and `uninstall` to run several tunnels side by side. --- ## CLI reference The `router` client is a single pure-Rust binary (currently v1.0.0) for macOS, Linux, and Windows. It has five subcommands: `run`, `install`, `uninstall`, `status`, and `update`. ### Installation - macOS & Linux: `curl -fsSL https://router.direct/install.sh | sh` (installs to `/usr/local/bin/router`) - Windows: `iwr https://router.direct/install.ps1 -UseBasicParsing | iex` (installs to `%LOCALAPPDATA%\router-direct\router.exe`) - Direct downloads: https://router.direct/bin/ ### Shared tunnel flags (`run` and `install`) | Flag | Description | Default | | --- | --- | --- | | `--server, -s` | Relay address to connect to, host:port. | required | | `--token, -t` | API key for the tunnel (`sk_live_…`). | required | | `--local, -l` | Local address to forward traffic to. Must be host:port. | `127.0.0.1:3000` | | `--subdomain, -d` | Request an HTTP tunnel on this subdomain. | — | | `--tcp-port, -p` | Request a TCP tunnel on this public port. | — | | `--udp-port, -u` | Request a UDP tunnel on this public port. | — | Exactly one of `--subdomain`, `--tcp-port`, or `--udp-port` must be given, and it must match the tunnel type of the API key. ### run Starts the tunnel in the foreground. The connection loop never exits on its own: disconnects are retried with exponential backoff (capped at 60 seconds), a 40-second watchdog reconnects if the relay goes silent, and the client answers relay heartbeats (30-second pings) automatically. Ctrl+C stops it. ``` router run --server 77.42.121.237:2333 --token sk_live_... --subdomain myapp --local 127.0.0.1:3000 router run --server 77.42.121.237:2333 --token sk_live_... --tcp-port 14090 --local 127.0.0.1:22 ``` ### install Registers the tunnel as a persistent system service and starts it immediately. The service invokes `router run` with the same flags, inheriting all reconnection behavior. - **Linux (systemd)** — system-wide unit when run as root, otherwise a user unit with linger enabled; OpenRC script on Alpine. - **macOS** — launchd agent with `RunAtLoad` and `KeepAlive`; logs at `~/Library/Logs/RouterDirect/`. - **Windows** — scheduled task at logon with an auto-restart wrapper in `%APPDATA%\RouterDirect\`. Extra flag: `--service-name` (default `router-direct`) — identifier for the service; use different names to run multiple tunnels side by side. ### uninstall Stops and removes the installed service and its wrapper files. Tunnel records are not deleted — remove those from the dashboard. ``` router uninstall router uninstall --service-name blog ``` ### status Prints whether the installed service is running, stopped, or failed. ### update Checks `https://dashboard.router.direct/api/version`; if a newer version exists, downloads the matching platform binary and replaces the current executable atomically with automatic rollback. The same check runs silently on every `run` startup. ``` router update # interactive router update --yes # skip the prompt ``` ### Configuration file Defaults are stored per OS in `config.toml`: - macOS: `~/Library/Application Support/com.router.direct/config.toml` - Linux: `~/.config/router-direct/config.toml` - Windows: `%APPDATA%\router-direct\config.toml` ```toml api_url = "https://dashboard.router.direct/api" default_server = "77.42.121.237:2333" [profiles."you@example.com"] email = "you@example.com" api_key = "sk_live_..." ``` If the file is missing, the client falls back to these defaults. --- ## Tunnel types in depth ### HTTP tunnels An HTTP tunnel gives you `https://.router.direct`. Nginx terminates TLS at the wildcard edge and forwards the request to the relay, which matches the Host header to your tunnel and streams it to your local web server. Subdomain rules: - 1–63 characters; lowercase letters, digits, and hyphens; no leading or trailing hyphen. - Reserved names are rejected: `dashboard`, `api`, `www`, `admin`, `status`, `docs`, and others. - Subdomains are unique across all users. If your local app generates absolute URLs, make sure it respects the `Host`/`X-Forwarded-*` headers so links and redirects point at your `*.router.direct` domain. ### TCP tunnels A TCP tunnel reserves a dedicated public port on the relay in the 10000–20000 range. The port is assigned automatically when you create the tunnel in the dashboard. Any inbound connection is piped straight to your local address — no protocol awareness. ``` router run --server 77.42.121.237:2333 --token sk_live_... --tcp-port 14090 --local 127.0.0.1:22 # → ssh -p 14090 user@77.42.121.237 ``` ### UDP tunnels A UDP tunnel reserves a public UDP port and relays datagrams to your local UDP socket. Each remote peer gets its own local socket, so connection-oriented UDP protocols like WireGuard work correctly. ``` router run --server 77.42.121.237:2333 --token sk_live_... --udp-port 51820 --local 127.0.0.1:51820 ``` ### Lifecycle & security - **Creation** — the dashboard writes the tunnel to PostgreSQL and mirrors `{id, type, subdomain, tcpPort, userId}` into Redis under `tunnel_key:`. - **Authentication** — the relay reads the key from Redis on the control connection and enforces a rate limit (10 failed attempts per 5 minutes per IP). - **Data channels** — every incoming connection is handled on a second TLS connection that must originate from the same IP as the control connection, preventing request-ID hijacking. - **Revocation** — deleting a tunnel removes the Redis key first (immediate relay rejection), then the PostgreSQL record. - **Encryption** — control and data channels between your machine and the relay are TLS; public HTTP traffic is TLS at the edge. UDP payloads travel inside the TLS data channel. ### Persistence & reconnection - **Exponential backoff** — retries at 2, 4, 8… seconds, capped at 60s; resets after a clean session. - **Watchdog** — if no bytes arrive from the relay for 40 seconds, the link is treated as dead and reconnected. - **Heartbeats** — the relay pings every 30 seconds and expects a pong within 45 seconds. - **Service supervision** — with `router install`, the OS service manager additionally restarts the client if the process dies and starts it after reboots. ### Multiple tunnels Create additional tunnels in the dashboard and install each under its own service name: ``` router install --server 77.42.121.237:2333 --token sk_live_AAA... --subdomain app --service-name app router install --server 77.42.121.237:2333 --token sk_live_BBB... --tcp-port 15000 --local 127.0.0.1:22 --service-name ssh ``` There is no per-tunnel fee. --- ## HTTP API Base URL: `https://dashboard.router.direct/api`. Sessions are cookie-based; tunnels are managed under the authenticated user. ### Authentication Signup and login set an HTTP-only session cookie (`router_session`) backed by Redis with a 7-day expiry. **POST /api/auth/signup** ```json { "email": "you@example.com", "password": "at-least-8-chars" } ``` Returns `{ "success": true, "email": "you@example.com" }` and sets the session cookie. Errors: 400 (missing fields, short password, invalid email, email already registered), 429 (rate limited — 3 attempts per 10 minutes per IP). **POST /api/auth/login** ```json { "email": "you@example.com", "password": "your-password" } ``` Same response shape as signup. Errors: 400 (invalid credentials), 429 (5 attempts per 5 minutes per IP). **POST /api/auth/logout** Clears the session cookie. No body required. ### Tunnels **POST /api/tunnels** — creates a tunnel and returns its API key. ```json { "tunnelType": "http", "subdomain": "myapp" } ``` ```json { "tunnelType": "tcp" } ``` - `tunnelType`: `"http"` or `"tcp"`. - `subdomain`: required for HTTP; must match `[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?` and not be reserved. - TCP tunnels receive a random free port in the 10000–20000 range. Response: ```json { "success": true, "tunnel": { "id": "...", "userId": "...", "apiKey": "sk_live_...", "tunnelType": "http", "subdomain": "myapp", "tcpPort": null, "createdAt": "..." } } ``` Errors: 400 (invalid type, missing subdomain, invalid/reserved subdomain, subdomain taken), 401 (not logged in), 500. **GET /api/tunnels** — lists the user's tunnels, newest first: `{ "tunnels": [...] }`. **DELETE /api/tunnels/:id** — deletes a tunnel (Redis key first, then PostgreSQL). Errors: 401, 403 (not yours), 404. **GET /api/version** — public. Latest CLI version and download URLs: ```json { "version": "1.0.0", "download_urls": { "aarch64-apple-darwin": "https://router.direct/bin/router-client-macos-arm64", "x86_64-apple-darwin": "https://router.direct/bin/router-client-macos-x86_64", "x86_64-unknown-linux-gnu": "https://router.direct/bin/router-client-linux-x86_64", "x86_64-pc-windows-msvc": "https://router.direct/bin/router-client-windows.exe" }, "changelog": "..." } ``` ### Relay wire protocol The CLI talks to the relay over raw TCP with newline-free JSON objects, TLS-encrypted, on `77.42.121.237:2333` (TLS server name `router.direct`). Control connection: ``` client → relay: { "type": "Control", "token": "sk_live_..." } relay → client: { "type": "AuthSuccess", "tunnel": { "Http": { "subdomain": "myapp" } } } relay → client: { "type": "AuthFailed", "reason": "Invalid API key" } ``` On success the tunnel config is one of `{"Http":{"subdomain":"…"}}`, `{"Tcp":{"port":14090}}`, or `{"Udp":{"port":51820}}`. The relay sends `Ping` every 30 seconds (answer with `{"type":"Pong"}`) and `NewConnection` for each inbound visitor: ``` { "type": "NewConnection", "request_id": "550e8400-e29b-41d4-a716-446655440000" } ``` For each `request_id` the client opens a second TLS connection to the relay, sends `{ "type": "Data", "request_id": "…" }`, and the relay pipes bytes bidirectionally. The data connection must originate from the same IP as the control connection. UDP tunnels keep one persistent data channel carrying length-prefixed frames of `[peer-address, payload]` pairs. Send each JSON message as a single write; the relay reads complete objects from the stream and may batch several into one TCP segment. --- ## Key facts - The CLI (`router`) is a pure-Rust binary, currently v1.0.0, for macOS, Linux, and Windows. - The relay control endpoint is TLS on `77.42.121.237:2333` with server name `router.direct`. - API keys use the form `sk_live_...`; the relay validates them against Redis, so revocation is instant. - HTTP tunnels are reachable at `https://.router.direct`; TCP and UDP tunnels get a public port in the 10000–20000 range on the relay. - Latest CLI version endpoint: https://dashboard.router.direct/api/version