Files
OmniRoute/docs/TUNNELS_GUIDE.md
diegosouzapw 20cb648ea9 docs(docs): expand v3.8.0 guides and add provider reference generator
Refresh the documentation set for v3.8.0 with new guides covering
authz, agent protocols, cloud agents, compliance, electron, evals,
guardrails, memory, skills, stealth, tunnels, webhooks, and more.

Add an auto-generated provider reference plus a
`gen:provider-reference` script to keep provider catalog docs aligned
with `src/shared/constants/providers.ts`.
2026-05-13 03:47:41 -03:00

12 KiB

Tunnels Guide

Source of truth: src/lib/{cloudflaredTunnel,ngrokTunnel,tailscaleTunnel}.ts, src/app/api/tunnels/ Last updated: 2026-05-13 — v3.8.0

OmniRoute can expose its local server (http://localhost:20128) to the public internet via three tunnel backends. This is useful for:

  • OAuth callbacks from cloud providers (Antigravity, Gemini, Cursor) that need a publicly reachable redirect URL.
  • Sharing your local instance with teammates without deploying a VM.
  • Mobile, remote, or cross-network testing.

All three backends are managed in-process — OmniRoute starts/stops the underlying binary or SDK from the dashboard or REST API. No reverse-proxy or systemd setup is required.

Backends at a glance

Backend Persistence Cost Setup
Cloudflare Quick Tunnel Ephemeral (URL changes each restart) Free Zero — auto-installs cloudflared
ngrok Stable while a paid plan or fixed domain is configured Free tier + paid Requires ngrok account + authtoken
Tailscale Funnel Stable per node within your tailnet Free for personal Requires Tailscale install + login + Funnel ACL

The implementations live in src/lib/cloudflaredTunnel.ts, src/lib/ngrokTunnel.ts, and src/lib/tailscaleTunnel.ts. All three return a common-shaped status object with phase, running, publicUrl, apiUrl, targetUrl, and lastError fields, so the dashboard can render them uniformly.

1. Cloudflare Tunnel (Quick Tunnel)

src/lib/cloudflaredTunnel.ts runs cloudflared tunnel --url http://localhost:<apiPort> as a child process and parses the assigned *.trycloudflare.com URL from stdout.

Key behaviors:

  • Auto-install. On first use, OmniRoute downloads the latest cloudflared binary from the official GitHub releases (managed install lives under DATA_DIR/cloudflared/). SHA256 of the downloaded asset is verified against the release manifest before execution.
  • Quick-tunnel only. The current implementation runs only the --url-style quick tunnel. Named/persistent tunnels (cloudflared tunnel login + cloudflared tunnel route dns ...) are not orchestrated by OmniRoute. URLs are ephemeral and will change every restart.
  • Process supervision. The cloudflared PID and resolved URL are persisted to cloudflared-state.json so the dashboard can resume status across reloads.

Enable / disable via REST

The endpoint uses an {action: "enable" | "disable"} body, not separate start/stop paths. Management auth (admin session or admin API key) is required.

# Enable
curl -X POST http://localhost:20128/api/tunnels/cloudflared \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=..." \
  -d '{"action":"enable"}'

# Status
curl http://localhost:20128/api/tunnels/cloudflared \
  -H "Cookie: auth_token=..."

# Disable
curl -X POST http://localhost:20128/api/tunnels/cloudflared \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=..." \
  -d '{"action":"disable"}'

Or via dashboard: Settings → Tunnels → Cloudflare.

Optional env vars

Variable Purpose
CLOUDFLARED_BIN Override the binary path. If set and valid, OmniRoute uses it instead of downloading.
CLOUDFLARED_PROTOCOL / TUNNEL_TRANSPORT_PROTOCOL Transport protocol (default http2).

2. ngrok

src/lib/ngrokTunnel.ts uses the @ngrok/ngrok SDK (in-process, no CLI subprocess). The native module is imported lazily on first start so platforms without prebuilt binaries do not break the app at boot.

Prerequisites

  1. Sign up at https://ngrok.com.
  2. Copy your authtoken from the ngrok dashboard.
  3. Provide it either via:
    • .env: NGROK_AUTHTOKEN=<token>, or
    • Dashboard: Settings → Tunnels → ngrok, or
    • REST body (one-shot): {"action":"enable","authToken":"<token>"}.

If neither is configured, status returns phase: "needs_auth".

Enable / disable via REST

# Enable (uses NGROK_AUTHTOKEN from env)
curl -X POST http://localhost:20128/api/tunnels/ngrok \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=..." \
  -d '{"action":"enable"}'

# Enable with inline token
curl -X POST http://localhost:20128/api/tunnels/ngrok \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=..." \
  -d '{"action":"enable","authToken":"2abc..."}'

# Status
curl http://localhost:20128/api/tunnels/ngrok \
  -H "Cookie: auth_token=..."

# Disable
curl -X POST http://localhost:20128/api/tunnels/ngrok \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=..." \
  -d '{"action":"disable"}'

The response includes the assigned publicUrl (e.g. https://abcd-1234.ngrok-free.app). Custom domains, regions, and policy rules must be configured in the ngrok dashboard — OmniRoute itself only forwards the local target URL to the SDK.

3. Tailscale Funnel

src/lib/tailscaleTunnel.ts orchestrates the system tailscale CLI to expose the local API port via Funnel (Tailscale's public-internet egress for serve). It supports the full lifecycle: install, login, daemon start, enable, disable.

The implementation invokes tailscale funnel --bg <port> (background mode). The public URL has the shape https://<machine>.<tailnet>.ts.net/.

Prerequisites

  1. Install Tailscale (or let OmniRoute do it — see install endpoint below).
  2. Sign in (tailscale login or via OmniRoute's login endpoint).
  3. Enable Funnel for your tailnet in the Tailscale admin console: https://login.tailscale.com/admin/settings/features.

On Linux and macOS the daemon (tailscaled) requires sudo to control. The POST endpoints accept an optional sudoPassword field which is forwarded to OmniRoute's MITM password cache (getCachedPassword / setCachedPassword) for the duration of the call. Windows uses the default service install at C:\Program Files\Tailscale\tailscale.exe.

REST endpoints

Tailscale has a richer surface than the other backends because installation, login, daemon, and tunnel are separate concerns.

Endpoint Method Purpose
/api/tunnels/tailscale GET Aggregated tunnel status (phase, tunnelUrl, apiUrl, etc.)
/api/tunnels/tailscale/check GET Lower-level check: installed? logged in? daemon running?
/api/tunnels/tailscale/install POST Install Tailscale (SSE-streamed progress events) — Linux/macOS
/api/tunnels/tailscale/start-daemon POST Start tailscaled on Linux/macOS
/api/tunnels/tailscale/login POST Begin login flow; returns authUrl to open in a browser
/api/tunnels/tailscale/enable POST Start the Funnel for the API port
/api/tunnels/tailscale/disable POST Stop the Funnel

All Tailscale endpoints require management auth (see routeUtils.ts :: requireTailscaleAuth).

Example enable:

curl -X POST http://localhost:20128/api/tunnels/tailscale/enable \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=..." \
  -d '{"sudoPassword":"<linux-pwd>","port":20128}'

If Funnel is not enabled in the admin console, the response includes funnelNotEnabled: true plus an enableUrl to open in a browser.

Optional env vars

Variable Purpose
TAILSCALE_BIN Override the tailscale binary path

Endpoint summary

Endpoint Method Body Auth
/api/tunnels/cloudflared GET management
/api/tunnels/cloudflared POST {action: "enable" | "disable"} management
/api/tunnels/ngrok GET management
/api/tunnels/ngrok POST {action, authToken?} management
/api/tunnels/tailscale GET management
/api/tunnels/tailscale/check GET management
/api/tunnels/tailscale/install POST {sudoPassword?} (SSE) management
/api/tunnels/tailscale/start-daemon POST {sudoPassword?} management
/api/tunnels/tailscale/login POST {hostname?} management
/api/tunnels/tailscale/enable POST {sudoPassword?, hostname?, port?} management
/api/tunnels/tailscale/disable POST {sudoPassword?} management

There is no central /api/settings/tunnels endpoint — each backend is independent.

OAuth callback considerations

When you expose OmniRoute through a tunnel, the dashboard and OAuth flows must build callback URLs against the public hostname, not localhost. Otherwise the OAuth provider redirects the user back to a URL its servers cannot reach, and the handshake fails.

Set:

NEXT_PUBLIC_BASE_URL=https://<your-tunnel-host>

and restart OmniRoute before initiating OAuth. For ephemeral Cloudflare Quick Tunnels the URL changes after every restart, so prefer ngrok with a reserved domain or Tailscale Funnel for production OAuth use.

Health and monitoring

The dashboard surfaces tunnel state under Settings → Tunnels:

  • Active backend(s) and current phase (stopped, starting, running, needs_auth, error).
  • The current public URL and the derived API URL (<publicUrl>/v1).
  • The local target URL the tunnel is forwarding to.
  • Last error message, if any.

For programmatic monitoring poll the per-backend GET endpoints. Running more than one backend simultaneously is allowed; OmniRoute will track each independently.

Troubleshooting

"cloudflared binary not found"

OmniRoute attempts to auto-install on first use. If the install is blocked (restricted network, no GitHub access), download cloudflared manually from https://github.com/cloudflare/cloudflared/releases and set CLOUDFLARED_BIN=/path/to/cloudflared.

"ngrok: authtoken required"

phase: "needs_auth" means no authtoken was found. Set NGROK_AUTHTOKEN in .env, configure it via the dashboard, or pass authToken in the enable POST body.

"tailscale: funnel not enabled"

When the enable response includes funnelNotEnabled: true, Funnel is disabled for your tailnet. Open the returned enableUrl (or the admin console feature page) and toggle Funnel on.

Tunnel URL changes break OAuth

Use ngrok with a reserved domain or Tailscale Funnel (both stable per-node). Cloudflare Quick Tunnels are ephemeral by design and not recommended for long-lived OAuth callbacks.

Permission denied on Linux/macOS for Tailscale

tailscaled needs root. Provide sudoPassword to the relevant POST endpoint, or run the daemon yourself (sudo systemctl start tailscaled).

See also