Cloudflare Tunnels for Node & Bun.
A typed, event-driven API and CLI over all three tunnel modes — with zero dependencies.
Website · npm · API reference · CLI reference · Examples · Issues
TunnelKit wraps the cloudflared binary into a typed library and a CLI command.
// API
const tk = new TunnelKit();
const { publicUrl } = await tk.quick.start({ service: 3000 });# CLI
tunnelkit quick 3000- All three modes — Quick, Remote (token), and Local (named), each behind its own namespace.
- Fully typed events —
TunnelKitandCloudflaredTunnelare typedEventEmitters. - Manages the binary — downloads
cloudflaredon demand, or reuses one onPATH. - Persistence by default — tunnels you start are saved; restore them by name, or disable with
store: false. - Zero dependencies — pure Node built-ins. Runs on Node 18+ and Bun.
| Mode | API | CLI |
|---|---|---|
| Quick | tk.quick.start({ service }) |
tunnelkit quick <port> |
| Remote | tk.remote.start({ id, token }) |
tunnelkit remote run --token <token> |
| Local | tk.local.start({ id, name, tunnelId, credentialsFile, ingress }) |
tunnelkit local run <name> --route <host>=<service> |
bun add @myrialabs/tunnelkit # library
bun add -g @myrialabs/tunnelkit # CLIcloudflared is downloaded on first use, or use one already on PATH.
Quick — a random *.trycloudflare.com URL, no account needed.
| API | CLI |
|---|---|
const { publicUrl } = await tk.quick.start({
service: 8080, autoStopMinutes: 30
}); |
tunnelkit quick 8080 --auto-stop 30 |
Remote — dashboard-managed tunnel, run from a token.
| API | CLI |
|---|---|
const { ingress } = await tk.remote.start({
id: 'my-app', token: process.env.CF_TUNNEL_TOKEN!
}); |
tunnelkit remote run --token "$CF_TUNNEL_TOKEN"
# tunnelkit remote run prod # reuse saved |
Local — named tunnel: authenticate once, create, route DNS, run.
| API | CLI |
|---|---|
await tk.local.login({ onUrl, onComplete, onError });
await tk.local.create('acme-prod');
await tk.local.routeDns('acme-prod', 'app.example.com');
await tk.local.start({ id, name, tunnelId, credentialsFile, ingress }); |
tunnelkit local login
tunnelkit local run my-app \
--route app.example.com=http://localhost:3000
# tunnelkit local run my-app # reuse saved |
tunnelkit with no arguments opens a live TUI — view all tunnels, stop/start, copy URLs, manage
saved configs.
tunnelkit > tunnels
❯ ● quick-5173 → https://abc.trycloudflare.com
○ quick-3000 → https://xyz.trycloudflare.com
● remotely-prod → 2 routes
- http://localhost:4001 → app.example.com
- http://localhost:4002 → api.example.com
● locally-prod → 2 routes
- http://localhost:5001 → shop.example.com
- http://localhost:5002 → blog.example.com
[↑/↓] select [n] new tunnel [x] stop [c] copy URL
[m] manage saved [q] quit
See the CLI reference for all keybindings.
tk.on('status-changed', (tunnels) => { });
tk.on('ingress-update', ({ id, ingress }) => { });
tk.on('connection', ({ id, info, status }) => { });The interactive panel shows live status per tunnel; --verbose prints diagnostics.
Remote and local tunnels auto-save to <dataDir>/config.json. Quick tunnels are ephemeral.
| API | CLI |
|---|---|
new TunnelKit(); // auto-save
new TunnelKit({ store: false }); // disable
await tk.restoreAll(); // rehydrate all |
tunnelkit saved # list saved
tunnelkit forget <name> # remove entry
# auto-save on remote run / local run |
| API | CLI |
|---|---|
tk.bin.status();
await tk.bin.ensure();
await tk.bin.install('2024.12.2'); |
tunnelkit status
tunnelkit install [version] |
| Option | API | CLI |
|---|---|---|
| Data directory | { dataDir } |
--data-dir |
| Install directory | { installDir } |
--install-dir |
| Store / persistence | { store: false } |
--no-save |
| Logger | { logger: console } |
--verbose |
| Quick timeout | { quickTimeoutMs: 15000 } |
— |
| Connection timeout | { connectTimeoutMs: 30000 } |
— |
| Skip confirm | — | --yes / -y |
import { CloudflaredTunnel } from '@myrialabs/tunnelkit';
const tunnel = CloudflaredTunnel.quick('http://localhost:3000');
tunnel.on('url', (url) => console.log(url));const shutdown = async () => { await tk.stopAll(); process.exit(0); };
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);From the CLI, Ctrl+C / q stops everything.
- API reference — every export, option, method, and event.
- CLI reference — every command, option, and example.
- Examples — runnable scenarios.
MIT License — see LICENSE for details.