# routeup: full docs > Stable HTTPS routes for local services. Public when you need it. This file is the concatenation of every published docs page. Source: https://routeup.dev --- ## routeup docs Source: https://routeup.dev/docs/index.md routeup gives local services stable HTTPS names and can expose those same routes publicly when needed. Local use is the default: run `routeup setup` once, then serve a local app at a trusted `https://.localhost` URL without DNS, accounts, or network access. Public exposure is available through a configured routeup server when an outside service needs to reach your machine. ## Where to start - New here? Start with [Install](/docs/getting-started/install) and [Your first route](/docs/getting-started/first-route). - Using a coding agent? Install the [Routeup Agent Skill](/docs/getting-started/agent-skill). - Need to see what reached your app? [Observe and debug requests](/docs/getting-started/observe-and-debug). - Building a specific workflow? Start with the [Recipes](/docs/recipes/webhook-development). - Want to understand the model? Read [Concepts › Routes](/docs/concepts/routes). - Want one live view? Open the [terminal dashboard](/docs/cli-reference/dashboard). - Looking for a specific command? Jump to the [CLI Reference](/docs/cli-reference/routeup). - Running your own server? See [Self-Hosting](/docs/self-hosting/running-the-server). - Common questions? Check the [FAQ](/docs/faq). ## What these docs cover These docs cover local HTTPS, public route claims, streaming/WebSockets/SSE, path routing, process running, request logs and inspection, the read-only terminal dashboard, automation, and self-hosting. --- ## Conflict resolution Source: https://routeup.dev/docs/architecture/conflict-resolution.md routeup prefers loud failure over silent last-wins. A surprise "last writer takes the route" is a common source of "why isn't my dev server responding" debugging, so conflicts are explicit. ## Local conflicts Two processes on one machine claiming the same route: ```txt default: fail closed; the CLI prints the owning pid and cwd orphans: the agent reaps stale claims when the owning pid is gone ``` For foreground or detached `routeup serve`, run `routeup stop [name]`. Runner and standalone exposure owners must be stopped from their owning terminal. You can also choose a different route name. Routes are owned by a live CLI process, which may be foreground or a detached serve owner. When that process exits, owner-conditional cleanup releases its claims and tears down matching tunnels; other claims are untouched. ## Public conflicts Two clients claiming the same route through the same server: ```txt default: 409 Conflict; generic message, owner identity not disclosed grace window: 30s after a disconnect the same token may resume the claim cross-token: a held route cannot be reclaimed by a different token public namespace: session-only, no grace window, first-come-first-served ``` Owner identity is never leaked to a non-owning client. A scoped token can only claim within its allow pattern, so conflicts across namespaces are impossible by construction. ## Why a grace window A brief network drop or a quick restart should not lose your public name to someone else. The 30-second grace lets the same token resume its claim after a disconnect. The public namespace deliberately skips this, its claims are session-only by design. See [Public namespace](/docs/concepts/public-namespace). --- ## CLI ↔ agent IPC Source: https://routeup.dev/docs/architecture/ipc.md The CLI and the local agent are separate processes. They talk over a per-user Unix domain socket, never a network port, so the local control plane is not reachable off the machine. ## Transport ```txt Transport: Unix domain socket, one per user Path: ~/.routeup/agent.sock $XDG_RUNTIME_DIR/routeup/agent.sock on Linux when available Permissions: 0700 directory, 0600 socket Wire format: JSON over HTTP/1.1 Auth: filesystem permissions only (no token on the local socket) Versioning: /v1/ URL prefix; GET /v1/status reports version and boot id ``` Filesystem permissions are the entire auth model here: only your user can read or write the socket, so no token is needed for local control. ## API surface ```txt POST /v1/routes register a route claim DELETE /v1/routes/{name} release a claim GET /v1/routes list active routes GET /v1/owners/{name} attach a serve-owner control stream POST /v1/owners/{name}/stop request cooperative owner shutdown POST /v1/owners/{name}/ack acknowledge stop receipt GET /v1/status agent status, version, uptime, boot id POST /v1/shutdown graceful shutdown GET /v1/logs?route=&follow= stream access logs (SSE) GET /v1/requests/{id} inspect a retained request/response exchange POST /v1/expose start public exposure for a claimed route POST /v1/unexpose stop public exposure ``` ## Why the registry can be in-memory The status response carries a `boot_id` generated once per agent process. Live CLI owners retain their exact desired state: serve and runner owners re-register local claims; any owner with public exposure restores it; standalone expose owners restore only their exposure. Foreground and detached owner processes are the source of truth, so the agent never persists the route registry or tunnel configuration to disk. `routeup stop` uses the owner stream and acknowledgment endpoints. It never signals a PID copied from the registry. Small non-secret runtime records contain only record ID, route, owner kind, and PID so lifecycle commands can fail closed while the in-memory agent is restarting; targets and credentials are not stored. See [Local agent](/docs/concepts/local-agent) for the daemon side and [Conflict resolution](/docs/architecture/conflict-resolution) for ownership rules. --- ## Local-only request flow Source: https://routeup.dev/docs/architecture/local-only-flow.md The local flow never leaves your machine. It needs no server, token, or network; `routeup setup` alone enables it. ## The path ```txt browser -> https://api.example-app.localhost -> local agent (terminates TLS, looks up the route) -> 127.0.0.1:8080 (your service) -> response back through the agent ``` ## Step by step 1. **Resolution.** `.localhost` is reserved by RFC 6761, so browsers and resolvers short-circuit `*.localhost` to `127.0.0.1` with no DNS lookup and no `/etc/hosts` editing. 2. **TLS.** The request arrives at the local agent on port 443. The agent terminates TLS using a certificate signed by the routeup local CA, which `setup` added to your trust store, so the browser shows no warning. 3. **Routing.** The agent matches the request's `Host` against its in-memory route registry to find the target port. 4. **Proxy.** It reverse-proxies to the local target (e.g. `127.0.0.1:8080`) and streams the response back. Because the registry is keyed by host, a dotted route like `api.example-app` works at any depth locally. The local CA mints a per-SNI leaf for the exact name. See [Local agent](/docs/concepts/local-agent) for the component, and [Public exposure flow](/docs/architecture/public-exposure-flow) for what changes when a route is exposed. --- ## Public exposure request flow Source: https://routeup.dev/docs/architecture/public-exposure-flow.md When a route is exposed, public requests arrive at the routeup server and travel back down an outbound tunnel to your local agent. Your service never opens a port to the internet. ## The path The example below assumes a root-tier token allowing `*.routeup.dev`. ```txt external browser / webhook provider -> https://example-app.routeup.dev -> public DNS -> routeup server -> authorization and active-claim lookup -> tunnel stream (WebSocket + yamux) -> local agent -> 127.0.0.1:8080 (your service) -> response returns over the same path ``` ## Step by step 1. **DNS.** The wildcard `*.routeup.dev` points at the server, so every public host resolves there. 2. **Server TLS.** The server terminates HTTPS with a wildcard certificate for the namespace base (`*.`). `.dev` is HSTS-preloaded, so the public side is HTTPS-only by design. 3. **Claim lookup.** The server maps the host to an active claim and its tunnel, rejecting unknown or unauthorized hosts. 4. **Tunnel.** Each public HTTP request becomes one yamux stream inside the client's outbound WebSocket connection on 443. The connection is **outbound** from your machine, so it survives NAT and most corporate, hotel, and mobile networks. 5. **Local delivery.** The local agent receives the stream and reverse-proxies it to the local target, exactly as in the [local flow](/docs/architecture/local-only-flow). Client-supplied forwarding aliases are removed at public ingress. The server sets trusted `X-Forwarded-For`, `X-Forwarded-Host`, and `X-Forwarded-Proto` values, and the agent preserves those values for the application. ## Why a tunnel The outbound connection means your service never needs an inbound port, a public IP, or a firewall change. Cancellation and timeouts propagate end-to-end, and the agent's tunnel client reconnects with backoff for transient drops. If the agent restarts or a tunnel terminates permanently, the live CLI owner restores the exposure from its retained desired state. See [Public server](/docs/concepts/public-server) and the [tunnel transport](/docs/concepts/public-server#what-it-handles) for the components involved. --- ## routeup completion Source: https://routeup.dev/docs/cli-reference/completion.md `routeup completion` generates a completion script for your shell. Install it once and tab-complete subcommands, flags, and live data from the agent. ## What completes - **Subcommands and flags** — always available, generated statically. - **`routeup logs [route]`** — completes active route names when the local agent is running. - **`routeup stop [route]`** — completes active route names; unsupported owner kinds still fail closed when invoked. - **`routeup inspect `** — completes captured request IDs when the local agent is running. If the agent is not running, positional completions return empty silently. ## Install ### Zsh ```bash routeup completion zsh > "${fpath[1]}/_routeup" ``` Add the following to your `~/.zshrc` if completions are not already enabled: ```bash autoload -U compinit && compinit ``` ### Bash ```bash # Per-user (most common): routeup completion bash > ~/.local/share/bash-completion/completions/routeup # System-wide: routeup completion bash | sudo tee /etc/bash_completion.d/routeup >/dev/null ``` Requires `bash-completion` v2. On macOS: `brew install bash-completion@2`. ### Fish ```bash routeup completion fish > ~/.config/fish/completions/routeup.fish ``` ### PowerShell This applies to PowerShell on supported macOS or Linux systems. routeup v1 does not support Windows. ```powershell routeup completion powershell | Out-String | Invoke-Expression ``` To persist, add the above line to your PowerShell profile (`$PROFILE`). --- ## routeup config Source: https://routeup.dev/docs/cli-reference/config.md `routeup config` explains what routeup resolved in the current directory: ```bash routeup config ``` Example output: ```txt frame="terminal" title="terminal" source: routeup.json path: /Users/me/example-app/routeup.json route: example-app targets: / http://localhost:8080 command: configured port env: PORT,WEBHOOK_CONSUMER_PORT exposure enabled: true expose paths: /api/webhooks/* capture: request,response redact headers: authorization,cookie server: https://edge.routeup.dev token: configured ``` The output combines project config with relevant environment and saved client settings. It reports only whether a token is configured; the token value is never printed. Use this command when a bare route name, target, runner command, exposure, or capture setting does not resolve as expected. `routeup config --json` emits the same resolved view for automation. --- ## routeup dashboard Source: https://routeup.dev/docs/cli-reference/dashboard.md `routeup dashboard` opens a full-screen terminal view of the local routeup agent: ```bash routeup dashboard ``` It shows active local routes, every managed public exposure, tunnel state, and a live bounded list of requests. Select a captured request and press `Enter` to open the same safely escaped request and response details available through [`routeup inspect`](/docs/cli-reference/inspect). ## Controls ```txt Tab / Shift-Tab move between routes, exposures, and requests Up / Down, j / k navigate the focused section g / G jump to the first or last item Enter inspect the selected captured request Esc / Backspace return from request details q / Ctrl-C quit ``` On short terminals, route and exposure sections collapse so the live request list remains usable. Columns also adapt to the terminal width. ## Read-only behavior The dashboard never starts, stops, or changes the agent, routes, or tunnels. If the agent is not running, it remains open with an offline state and reconnects when the agent becomes available. Request history resets when the agent restarts because logs and captures are held only in memory. The command requires an interactive terminal. For scripts and redirected output, use [`routeup routes --json`](/docs/cli-reference/routes) and [`routeup logs --json`](/docs/cli-reference/logs) instead. --- ## routeup doctor Source: https://routeup.dev/docs/cli-reference/doctor.md `routeup doctor` checks that the pieces [setup](/docs/cli-reference/setup) installed are still healthy. Run it whenever a local route does not load as expected. ```bash routeup doctor ``` ## What it checks - **Local CA**: the certificate authority exists under `~/.routeup`. - **OS trust**: that CA is present in the system trust store. - **Port 443**: routeup can answer on 443 (the macOS forwarder is installed, or the Linux `cap_net_bind_service` capability is set on the binary). - **Agent**: the background agent is running and reachable over its local socket. Each check prints `ok`, `warn`, or a specific failure with the command that fixes it: ```txt frame="terminal" title="terminal" [ok] local CA at /Users/me/.routeup/ca.crt (expires 2027-08-11) [ok] local CA trusted in OS trust store [ok] port 443: helper installed [ok] agent running (version 0.4.2, uptime 5127s) ``` The command exits successfully when every check is `ok` or `warn`, and fails when any check is `fail`. Use `routeup doctor --json` for structured check IDs, levels, messages, and an overall `healthy` boolean. ## Common failures - **Lost Linux capability after an upgrade**: a package upgrade replaces the binary inode, dropping the `setcap` capability. `doctor` detects this with `getcap`; re-run `routeup setup` to reapply it. - **CA not trusted**: the trust store was reset or the CA was removed. Re-run `routeup setup`. - **Agent unreachable**: restart it with `routeup agent restart`, or run `routeup setup` if setup state is missing or damaged. ## Non-browser clients Browsers and `curl` usually use the system trust store automatically. Some runtimes ship their own CA bundle; point them at the routeup CA: ```bash export NODE_EXTRA_CA_CERTS=~/.routeup/ca.crt # Node.js export REQUESTS_CA_BUNDLE=~/.routeup/ca.crt # Python (requests / urllib3) ``` For apps with their own certificate settings, including Hoppscotch, see [Custom CA certificates](/docs/configuration/custom-ca-certificates). --- ## routeup exec Source: https://routeup.dev/docs/cli-reference/exec.md `routeup exec` runs one command with the Routeup project environment. It does not start the agent, register or expose a route, allocate a port, or wait for a service to become ready. Use it when [`routeup serve`](/docs/cli-reference/serve) owns the route and other processes need the same URLs or local CA trust: ```bash # Terminal 1: own the route routeup serve # Other terminals: run independent processes with the project environment routeup exec -- yarn start:dev routeup exec -- yarn start:consumer:sync routeup exec -- yarn start:consumer:webhook ``` Stopping an `exec` command does not remove the route. The route remains active until the separate `routeup serve` process stops. ## Environment `exec` preserves the existing environment and adds Routeup's project context: ```txt HOST=127.0.0.1 PORT= ROUTEUP_LOCAL_URL=https://example-app.localhost ROUTEUP_URL=https://example-app.localhost NODE_EXTRA_CA_CERTS=/Users/me/.routeup/ca.crt ``` The project-local `node_modules/.bin` directory is also prepended to `PATH`. When the route already has an active public exposure, `ROUTEUP_URL` contains that public URL instead. `ROUTEUP_LOCAL_URL` always stays local. `ROUTEUP_TOKEN` is not inherited by the child command. These values are a snapshot taken when the process starts. A later exposure change does not modify the environment of a running process. ## Command resolution With an explicit command, put every command argument after `--`: ```bash routeup exec -- yarn test routeup exec -- node scripts/seed.js ``` The explicit argument vector is executed without shell interpolation. It takes precedence over a configured runner command. With no arguments, `exec` uses the same configured command as bare `routeup`: ```bash routeup exec ``` - A `package.json` `routeup.script` resolves the named package script. - A `routeup.json` `command` runs as a shell command. - If neither is configured, pass a command after `--`. ## Process behavior `exec` owns only the process it starts. It forwards terminal input and signals, stops remaining descendants when the command exits, and returns the child's exit code. It does not restart the command or coordinate it with other processes. Managed cancellation escalates to `SIGKILL` after ten seconds when the process group does not stop. The injected URL describes the project route but does not guarantee it is active. Run `routeup serve` separately when the command needs to reach that route. --- ## routeup expose Source: https://routeup.dev/docs/cli-reference/expose.md `routeup expose` opens a public tunnel without registering or holding a new local route. Run `routeup setup` before first use so the local agent and machine setup exist. An explicit route name is literal. When it is omitted, routeup uses `ROUTEUP_NAME`, config `name`, then the working-directory basename. If no `--port`, `--target`, or `ROUTEUP_PORT` override is supplied, it first looks for an active local route with the same name and reuses that route's targets. If no matching active route exists, it resolves targets from flags, environment, or project config. ```bash routeup expose example-app --port 8080 ``` With an explicit or config-only target, the exposure is public-only: it does not create or advertise `https://example-app.localhost`. When it reuses an existing active route, it also prints that route's local URL. Example output for an explicit target: This example assumes a root-tier token allowing `*.routeup.dev`. ```txt frame="terminal" title="terminal" public: https://example-app.routeup.dev targets: / http://localhost:8080 expose: all paths press Ctrl-C to stop ``` `routeup serve example-app --port 8080 --expose` is different: `serve` first registers and holds the local route, then exposes those same targets publicly. `--expose` can also be written as `--public`. ## Flags ```txt --port root target port --target path target; repeat for multiple targets --server public-server control URL --token server token --random generate a random route name --json write one ready event as JSON --qr print a terminal QR code for the public URL ``` `--json` and `--qr` are mutually exclusive. ## Automation output Use `--json` for a pure machine-readable ready event: ```json title="ready event" {"event":"ready","route":"example-app","public_url":"https://example-app.routeup.dev","targets":[{"path":"/","port":8080}]} ``` The command continues holding and reconciling the exposure after writing the single ready event. It restores the tunnel after an agent restart and remains attached until Ctrl-C or termination. `routeup stop` does not control standalone exposures. ## Random public names Use `--random` to generate a random route name. The server places its normalized public label under the granted namespace: ```bash routeup expose --random --port 8080 ``` Without a token, this uses the server's public namespace when one is enabled and the resulting claim is session-only. With a token, the random label is placed under the token's allowed namespace. ## Server and token Pass the server and token directly: ```bash routeup expose example-app --port 8080 --server https://edge.routeup.dev --token sk_routeup_xxx ``` Or configure them once: ```bash routeup setup --server https://edge.routeup.dev --token sk_routeup_xxx routeup expose example-app --port 8080 ``` Environment variables work too: ```bash export ROUTEUP_SERVER=https://edge.routeup.dev export ROUTEUP_TOKEN=sk_routeup_xxx routeup expose example-app --port 8080 ``` --- ## routeup inspect Source: https://routeup.dev/docs/cli-reference/inspect.md `routeup inspect` shows the retained request and response data for a request that was recorded by an opted-in route. ```bash routeup inspect ``` Get the request id from `routeup logs`: ```bash routeup logs example-app --limit 1 ``` ```txt frame="terminal" title="terminal" TIME ROUTE SOURCE STATUS ID METHOD DURATION PATH 12:41:03 example-app public 200 req_Ap7kQ3mN8vR2xLzC POST 38ms /api/webhooks/github ``` ```bash routeup inspect req_Ap7kQ3mN8vR2xLzC ``` ## Example output ```txt frame="terminal" title="terminal" Request req_Ap7kQ3mN8vR2xLzC Metadata -------- Source: public Route: example-app Target: /:8080 Status: 200 Duration: 38ms Method: POST Path: /api/webhooks/github Host: example-app.try.routeup.dev Request ------- Capture: complete Body bytes: 63 Redacted: authorization, cookie, x-hub-signature-256 X-Github-Event: push Body ---- { "ref": "refs/heads/main", "repository": { "full_name": "acme/api" } } Response -------- Capture: complete Body bytes: 11 Redacted: authorization, cookie, x-hub-signature-256 Content-Type: application/json Body ---- { "ok": true } ``` ## Export as curl Export a complete captured request as a curl command targeting its corresponding local `.localhost` URL: ```bash routeup inspect req_Ap7kQ3mN8vR2xLzC --curl ``` ```bash frame="terminal" title="terminal" curl \ --ipv4 \ --path-as-is \ --request 'POST' \ --header 'Content-Type: application/json' \ --header 'X-Github-Event: push' \ --data-raw '{"ref":"refs/heads/main"}' \ --url 'https://example-app.localhost/api/webhooks/github' ``` `--path-as-is` is a boolean curl option that prevents curl from normalizing the captured path; the complete path and query remain in `--url`. Safe text bodies are embedded directly, while binary bodies use a base64 decode pipe. Every retained header is included. Headers listed in `redact_headers` remain absent because routeup never stored them. The generated command can therefore contain credentials or other sensitive captured values when they were not redacted. Curl export requires a complete request capture, and its local route must be active when you execute the command. ## Enabling capture Capture is disabled by default. Enable it per-route in `routeup.json`: ```json title="routeup.json" { "name": "example-app", "port": 8080, "capture": { "request": true, "response": true, "redact_headers": ["authorization", "cookie", "x-hub-signature-256"] } } ``` `request` captures incoming headers and body. `response` captures the upstream's headers and body. Both are independent; you can enable either or both. Headers listed in `redact_headers` are omitted from the captured copy but are still forwarded to the upstream service unchanged. ## Retention Captured data is stored in the same in-memory ring buffer as request logs (last 1,024 entries). It is cleared when the agent restarts. Each captured request and response message is independently bounded to 256 KiB. Larger bodies are captured as a prefix and marked `partial`. ## Privacy `routeup inspect` is only accessible over the local Unix socket (mode 0600). It is not reachable from the network. By default, captured bytes and header values are escaped before they reach the terminal, so a captured payload cannot emit terminal control sequences. Use `--raw` only when you intentionally need the original bytes, usually with redirection. `--json` emits the complete structured exchange; captured byte slices use JSON's base64 encoding. `--raw`, `--json`, and `--curl` are mutually exclusive. --- ## routeup logs Source: https://routeup.dev/docs/cli-reference/logs.md `routeup logs` shows request logs recorded by the local agent. The agent is the authoritative log for traffic that reaches a local route or live tunnel. Requests rejected by the public server before reaching the tunnel are not present. ```bash routeup logs routeup logs example-app routeup logs api.example-app --follow ``` Default output: ```txt frame="terminal" title="terminal" TIME ROUTE SOURCE STATUS ID METHOD DURATION PATH 12:41:03 example-app public 200 req_Ap7kQ3mN8vR2xLzC POST 38ms /api/webhooks/github 12:41:07 example-app local 200 req_9fKq2LmXw8rT4Yb1 GET 432us /settings ``` Each line shows the time, route, source (`local` or `public`), status, request id, method, latency, and path. ## Follow mode `--follow` streams new matching requests as they arrive, one line per request. If that stream ends because the agent stops or restarts, rerun the command. The full-screen [`routeup dashboard`](/docs/cli-reference/dashboard) and foreground [`routeup serve`](/docs/cli-reference/serve) use reconnecting live-log models. ```bash routeup logs example-app --public --follow ``` Output is composable: pipe to `grep`, `jq`, or `tee` freely. Use `--json` for machine-readable NDJSON. Human rows use fixed-width columns and clip overlong values so `PATH` remains aligned. Positive durations below one microsecond display as `<1us`; durations below one millisecond retain integer microsecond precision such as `432us` or `999us`; durations at or above one millisecond are rounded to the nearest millisecond. A nonpositive duration displays as `0ms`. ## Flags ```txt --follow stream new matching requests --public only public tunnel requests --local only local .localhost requests --since requests since a duration such as 10m or an RFC3339 time --method only this HTTP method --status only this HTTP status code --limit at most the newest N matching requests --json emit one JSON request record per line ``` ```bash # Watch incoming webhooks in real time routeup logs example-app --follow --public ``` `--public` and `--local` are mutually exclusive. `--json` emits NDJSON and can be combined with `--follow`; it produces no placeholder record when there are no matches or the agent is not running. ## Retention Logs live in an in-memory ring buffer of the last 1,024 entries. They are not written to disk, and they are cleared when the agent restarts. ## Privacy Headers and bodies are not included in request logs, only the metadata above. To see full headers and bodies for a specific request, enable [capture](/docs/configuration/routeup-json#request-and-response-capture) on the route and use [`routeup inspect `](/docs/cli-reference/inspect). --- ## Operator commands Source: https://routeup.dev/docs/cli-reference/operator.md These commands run the public server and manage its tokens. They are hidden from the default `routeup --help` because normal users never need them. Everything here is for the operator of a deployment, see [Self-hosting](/docs/self-hosting/running-the-server) for the full guide. ## Run the server ```bash CLOUDFLARE_API_TOKEN=… \ routeup server --domain tunnel.example.com --reserved edge \ --db /var/lib/routeup/server.db ``` Key flags: ```txt --domain public suffix, e.g. tunnel.example.com --config server config file --db SQLite database path --public-namespace