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 thecloudflaredbinary, 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/ngrokpackage 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:
src/app/api/tunnels/cloudflared/route.ts– Exposes/api/tunnels/cloudflared/start,/status, and/stop.src/app/api/tunnels/ngrok/route.ts– Exposes/api/tunnels/ngrok/start,/status, and/stop.
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.tsandsrc/lib/ngrokTunnel.ts, ensuring tunnel failures do not crash the LLM router. - Cloudflare Quick Tunnels require the
cloudflaredbinary in yourPATHand support optionalhostnameandapiTokenparameters. - ngrok tunnels require an authtoken (via param, config file, or env var), accept
addr(port) andregionoptions, and dynamically import the SDK to avoid hard dependencies. - Use
getCloudflaredTunnelStatusorgetNgrokTunnelStatusto poll public URLs andstopCloudflaredTunnel/stopNgrokTunnelto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →