Skip to content

myrialabs/tunnelkit

Repository files navigation

TunnelKit

TunnelKit

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

npm version License: MIT Node 18+ and Bun


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

Why TunnelKit

  • All three modes — Quick, Remote (token), and Local (named), each behind its own namespace.
  • Fully typed eventsTunnelKit and CloudflaredTunnel are typed EventEmitters.
  • Manages the binary — downloads cloudflared on demand, or reuses one on PATH.
  • 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.

Quick start

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      # CLI

cloudflared is downloaded on first use, or use one already on PATH.

The three modes

Quick — a random *.trycloudflare.com URL, no account needed.

APICLI
const { publicUrl } = await tk.quick.start({
  service: 8080, autoStopMinutes: 30
});
tunnelkit quick 8080 --auto-stop 30

Remote — dashboard-managed tunnel, run from a token.

APICLI
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.

APICLI
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

Interactive panel

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.

Events

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.

Persistence

Remote and local tunnels auto-save to <dataDir>/config.json. Quick tunnels are ephemeral.

APICLI
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

Binary management

APICLI
tk.bin.status();
await tk.bin.ensure();
await tk.bin.install('2024.12.2');
tunnelkit status
tunnelkit install [version]

Options

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

Low-level API

import { CloudflaredTunnel } from '@myrialabs/tunnelkit';
const tunnel = CloudflaredTunnel.quick('http://localhost:3000');
tunnel.on('url', (url) => console.log(url));

Graceful shutdown

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.

Documentation

License

MIT License — see LICENSE for details.

About

Run Cloudflare Tunnels (Quick, Remote & Local) from Node.js or Bun with a typed API and CLI

Topics

Resources

License

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors