How to Set Up Tunnels with Cloudflare Quick Tunnels or ngrok in OmniRoute

OmniRoute exposes your local router to the internet via built-in Cloudflare Quick Tunnels and ngrok support, using the modular handlers in src/lib/cloudflaredTunnel.ts and src/lib/ngrokTunnel.ts that you can control programmatically or through REST API endpoints.

Setting up public endpoints for local development or remote testing is a core feature of OmniRoute. This guide explains how to configure and manage both Cloudflare Quick Tunnels and ngrok using the tunneling subsystem implemented in the release/v3.8.49 branch.

Architecture Overview

OmniRoute isolates tunnel management from the main request pipeline to ensure that tunnel failures do not affect the LLM routing engine. The architecture consists of provider-specific core modules and corresponding HTTP route handlers.

Core Tunnel Modules

The tunnel logic resides in dedicated library files that dynamically import binaries only when needed:

  • src/lib/cloudflaredTunnel.ts – Launches the cloudflared binary, parses its stdout to capture the public URL, and maintains a global listener handle for lifecycle management.
  • src/lib/ngrokTunnel.ts – Dynamically imports the @ngrok/ngrok package only when needed, creates a forward listener, and stores the connection state.
  • src/lib/tunnelManager.ts (if present) – Provides a central registry that abstracts over both providers, letting downstream code query the current public URL without knowing which provider is active.

Both provider modules expose standardized functions—start, getStatus, and stop—making them interchangeable at the API layer.

API Route Handlers

HTTP endpoints wrap the core modules for external access:

These routes accept JSON payloads matching the programmatic function signatures and return objects containing publicUrl and optional error fields.

Setting Up Cloudflare Quick Tunnels

Cloudflare Quick Tunnels provide ephemeral public URLs without requiring a registered domain. OmniRoute automates the cloudflared CLI interaction.

Prerequisites

Ensure the cloudflared binary is installed on your system and accessible in your PATH. OmniRoute launches it as a subprocess and parses its output to extract the public URL. If the binary is missing, the start function will fail with a clear error message.

Starting the Tunnel Programmatically

Import the startCloudflaredTunnel function from the core module and await the returned public URL:

import { startCloudflaredTunnel } from '@/lib/cloudflaredTunnel';

const result = await startCloudflaredTunnel({
  hostname: 'my-router.trycloudflare.com', // optional custom subdomain
  apiToken: process.env.CLOUDFLARED_TOKEN,  // optional Cloudflare API token
});

console.log('Tunnel active at:', result.publicUrl);

If the binary fails to obtain a URL, the function throws the specific error: "cloudflared could not obtain a public URL."

Checking Status and Stopping

Query the active tunnel state using getCloudflaredTunnelStatus, which returns an object with publicUrl and optional error properties. Terminate the tunnel with stopCloudflaredTunnel:

import {
  getCloudflaredTunnelStatus,
  stopCloudflaredTunnel,
} from '@/lib/cloudflaredTunnel';

const status = await getCloudflaredTunnelStatus();
console.log('Current URL:', status.publicUrl ?? status.error);

// Tear down the tunnel
await stopCloudflaredTunnel(); // clears the global listener and terminates the process

Setting Up ngrok Tunnels

ngrok offers TCP and HTTP tunnels with custom region selection. OmniRoute integrates via the official @ngrok/ngrok Node.js SDK.

Authentication Setup

Before starting, configure your ngrok authtoken. The module checks for credentials in three locations: the authtoken parameter passed to startNgrokTunnel, the standard ngrok configuration file at ~/.ngrok2/ngrok.yml, or the NGROK_AUTHTOKEN environment variable. Missing credentials surface the specific error: "An ngrok authtoken is required."

Starting the Tunnel

Invoke startNgrokTunnel with the local port and optional region:

import { startNgrokTunnel } from '@/lib/ngrokTunnel';

const tunnel = await startNgrokTunnel({
  addr: 3000,    // The port OmniRoute listens on
  region: 'eu',  // Optional: us, eu, ap, au, sa, jp, in
});

console.log('ngrok URL:', tunnel.publicUrl);

Monitoring and Stopping

Query the tunnel status and terminate the session using the corresponding status and stop functions:

import {
  getNgrokTunnelStatus,
  stopNgrokTunnel,
} from '@/lib/ngrokTunnel';

const status = await getNgrokTunnelStatus();
console.log('ngrok status:', status.publicUrl ?? status.error);

// Shut down the tunnel
await stopNgrokTunnel(); // closes the ngrok process and clears the listener

Using the HTTP API Alternative

If you prefer command-line scripts or CI integrations, use the REST endpoints instead of importing modules directly. The CLI (omniroute) forwards identical payloads to these routes.

Method Endpoint Body Description
POST /api/tunnels/cloudflared/start { "hostname": "...", "apiToken": "..." } Starts a Cloudflare Quick Tunnel
GET /api/tunnels/cloudflared/status Returns { publicUrl, error }
POST /api/tunnels/cloudflared/stop Stops the active Cloudflare tunnel
POST /api/tunnels/ngrok/start { "addr": 3000, "region": "eu" } Starts an ngrok tunnel
GET /api/tunnels/ngrok/status Returns { publicUrl, error }
POST /api/tunnels/ngrok/stop Stops the active ngrok tunnel

These routes are defined in src/app/api/tunnels/cloudflared/route.ts and src/app/api/tunnels/ngrok/route.ts, according to the OmniRoute source code.

Summary

  • OmniRoute provides isolated tunnel management via src/lib/cloudflaredTunnel.ts and src/lib/ngrokTunnel.ts, ensuring tunnel failures do not crash the LLM router.
  • Cloudflare Quick Tunnels require the cloudflared binary in your PATH and support optional hostname and apiToken parameters.
  • ngrok tunnels require an authtoken (via param, config file, or env var), accept addr (port) and region options, and dynamically import the SDK to avoid hard dependencies.
  • Use getCloudflaredTunnelStatus or getNgrokTunnelStatus to poll public URLs and stopCloudflaredTunnel/stopNgrokTunnel to terminate processes cleanly.
  • HTTP endpoints at /api/tunnels/{provider}/ expose identical functionality for non-JavaScript consumers and CI pipelines.

Frequently Asked Questions

Do I need to install cloudflared separately?

Yes. OmniRoute expects the cloudflared binary to exist in your system PATH. The framework does not bundle the binary but launches it as a subprocess via src/lib/cloudflaredTunnel.ts, parsing stdout to extract the public URL. If the binary is missing, startCloudflaredTunnel will fail with an actionable error message.

Where does OmniRoute store the ngrok authtoken?

The ngrok module in src/lib/ngrokTunnel.ts checks for the authtoken in three locations, in order: the authtoken parameter passed directly to startNgrokTunnel, the standard ngrok configuration file at ~/.ngrok2/ngrok.yml, or the NGROK_AUTHTOKEN environment variable. If none are found, the function rejects with "An ngrok authtoken is required."

Can I run both Cloudflare and ngrok tunnels simultaneously?

Technically yes, because each provider maintains its own global listener state in its respective module. However, only one tunnel per provider type can be active at a time because the stop functions clear the global handle. To run both simultaneously, start each with its respective start function and manage their lifecycles independently via stopCloudflaredTunnel and stopNgrokTunnel.

What happens if the tunnel process crashes while OmniRoute is running?

The tunnel modules are deliberately isolated from the main request pipeline. According to the OmniRoute source code, a crashed cloudflared or ngrok process will not terminate the LLM routing engine. The status endpoints will reflect the failure via the error field, allowing your application to detect the outage and attempt a restart without restarting the entire OmniRoute server.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →