npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tuinnel

v0.1.1

Published

Cloudflare Tunnel manager with TUI dashboard

Downloads

20

Readme

tuinnel

A TUI-first tool for managing Cloudflare Tunnels. Expose your local dev servers to the internet with custom subdomains on your own domains — add, edit, delete, start, and stop tunnels all from an interactive dashboard.

$ tuinnel

┌─────────────────┬──────────────────────────────────────┐
│  TUNNELS        │  [1:Details] [2:Logs] [3:Metrics]    │
│  ◉ app :3000    │                                      │
│  ◌ api :8080    │  Status: ◉ Connected  Uptime: 00:12  │
│                 │  Public: https://app.mysite.com      │
└─────────────────┴──────────────────────────────────────┘
 a Add  d Delete  e Edit  s Start/Stop  r Restart  ? Help

Features

  • TUI dashboard — Full interactive dashboard; add, edit, delete tunnels without leaving the terminal
  • Custom domains — Map local ports to subdomains on domains you own (app.mysite.com, api.mysite.com)
  • Quick tunnels — Zero-config tunnels via trycloudflare.com (no account needed)
  • Persistent state — Tunnels auto-restart between sessions based on last known state
  • Inline onboarding — Setup wizard on first run, no separate CLI step needed
  • Multi-tunnel — Run multiple tunnels simultaneously with a sidebar to switch between them
  • Smart defaults — Auto-detects frameworks from package.json and suggests subdomain names (port 4200 = "angular", 5173 = "vite", etc.)
  • Managed binary — Automatically downloads and manages the cloudflared binary
  • Auto-HTTPS detection — Probes local ports to detect self-signed HTTPS and configures tunnels accordingly
  • Atomic config — Config stored in ~/.tuinnel/config.json with atomic writes and 0600 permissions
  • Diagnosticstuinnel doctor validates your token, permissions, binary, and network connectivity

Requirements

  • Node.js 20+ (runtime)
  • macOS or Linux (darwin-arm64, darwin-x64, linux-arm64, linux-x64)
  • A Cloudflare account with at least one domain (for named tunnels; quick tunnels work without an account)

Installation

npm install -g tuinnel

Quick Start

# Open interactive dashboard (first run shows setup wizard)
tuinnel

# Quick start a tunnel on port 3000
tuinnel 3000

Zero-config (no account needed)

Expose a local server with a random public URL:

tuinnel up 3000 --quick
# => https://random-words.trycloudflare.com <- :3000

Custom domains (requires Cloudflare account)

# 1. Open tuinnel — the setup wizard runs automatically on first launch
tuinnel

# 2. Or set up via CLI (one-time)
tuinnel init

# 3. Start a tunnel via CLI
tuinnel up 3000
# => https://app.mysite.com <- :3000

Creating a Cloudflare API Token

tuinnel requires a scoped API token (not a Global API Key) with specific permissions. Follow these steps to create one:

Step 1: Open the Cloudflare Dashboard

Go to https://dash.cloudflare.com/profile/api-tokens and click Create Token.

Step 2: Start from a blank template

Scroll past the pre-built templates and click Create Custom Token at the bottom of the page. Give it a descriptive name like tuinnel or tunnel-manager.

Step 3: Configure permissions

Add these three permissions. All three are required:

| Permission | Access | Why it's needed | |---|---|---| | Zone > Zone > Read | Read | List your domains so tuinnel can discover zones and account IDs | | Zone > DNS > Edit | Edit | Create and delete CNAME records that point subdomains to your tunnels | | Account > Cloudflare Tunnel > Edit | Edit | Create, configure, and delete named tunnels |

Your permissions table should look like this:

+----------+-------------------+------+
| Zone     | Zone              | Read |
| Zone     | DNS               | Edit |
| Account  | Cloudflare Tunnel | Edit |
+----------+-------------------+------+

Step 4: Set zone and account resources

Under Zone Resources, choose which zones (domains) the token can access:

  • All zones — If you want tuinnel to work with any domain in your account
  • Specific zone — If you want to restrict the token to a single domain (recommended for tighter security)

Under Account Resources, select the account that owns your zones.

Step 5: (Optional) Restrict client IP addresses

For additional security, you can restrict the token to only work from your IP address or IP range under Client IP Address Filtering. This is optional but recommended for production use.

Step 6: Set TTL (optional)

You can set a start and end date for the token. Leave blank for a non-expiring token.

Step 7: Create and copy

Click Continue to summary, review the permissions, then click Create Token.

Copy the token immediately. Cloudflare will only show it once. If you lose it, you'll need to create a new one.

Step 8: Configure tuinnel

Run tuinnel and the setup wizard will guide you through token configuration:

tuinnel

Or use the CLI setup command:

tuinnel init

When prompted, paste your token. tuinnel will:

  1. Validate the token against the Cloudflare API
  2. List your available zones (domains)
  3. Let you pick a default zone
  4. Save the config to ~/.tuinnel/config.json

Alternative: Environment variable

Instead of storing the token in the config file, you can set it as an environment variable:

export CLOUDFLARE_API_TOKEN="your-token-here"

Or use the tuinnel-specific variable:

export TUINNEL_API_TOKEN="your-token-here"

Environment variables take priority over the config file.

Verifying your token

Run diagnostics to confirm everything is set up correctly:

tuinnel doctor

Expected output:

tuinnel doctor

  PASS  Config file
         Found at ~/.tuinnel/config.json
  PASS  API token
         Token found (config file, ending ...ab1c)
  PASS  Token validates
         Valid. Access to 2 zones
  PASS  cloudflared binary
         Version 2025.8.0 (managed)
  PASS  Network connectivity
         Cloudflare API reachable (HTTP 200)

All 5 checks passed.

Common token issues

| Symptom | Cause | Fix | |---|---|---| | Authentication failed | Token is invalid, revoked, or expired | Create a new token at the API tokens page | | Insufficient permissions | Token is missing one or more required permissions | Edit the token and add the missing permission (Zone:Read, DNS:Edit, or Cloudflare Tunnel:Edit) | | No zones found | Token doesn't have access to any zones | Edit the token's Zone Resources to include your domain | | This looks like a Global API Key | You pasted the 37-character Global API Key instead of a scoped token | Go to the API tokens page and create a new API Token (not the Global API Key shown at the top) |

Commands

| Command | Description | |---|---| | tuinnel | Open interactive TUI dashboard | | tuinnel <port> | Quick start: create tunnel and open dashboard | | tuinnel init | Interactive setup wizard for API token and default zone | | tuinnel up [ports...] | Start tunnels and open the TUI dashboard | | tuinnel up <port> --quick | Start an ephemeral quick tunnel (no account needed) | | tuinnel up <port> --no-tui | Start tunnels with plain log output instead of TUI | | tuinnel down [names...] | Stop running tunnels | | tuinnel down --all | Stop all running tunnels | | tuinnel down <name> --clean | Stop a tunnel and delete its DNS record and tunnel from Cloudflare | | tuinnel add <port> | Add a tunnel mapping to config (does not start it) | | tuinnel add <port> --subdomain <name> | Add with explicit subdomain (non-interactive) | | tuinnel add <port> --adopt | Adopt an existing Cloudflare tunnel | | tuinnel remove <name> | Remove a tunnel mapping from config | | tuinnel list | List all configured tunnels | | tuinnel status | Show running tunnels with health status | | tuinnel zones | List available Cloudflare zones (domains) | | tuinnel doctor | Run diagnostics (token, permissions, binary, network) | | tuinnel purge | Find and remove orphaned tunnels and DNS records |

Aliases

| Alias | Equivalent | |---|---| | tuinnel start | tuinnel up | | tuinnel stop | tuinnel down | | tuinnel rm | tuinnel remove | | tuinnel ls | tuinnel list |

Flags

| Flag | Scope | Description | |---|---|---| | --verbose | Global | Enable verbose output | | --json | list, status, zones | Output in JSON format | | -v, --version | Global | Show version | | --help | Global | Show help for any command |

TUI Keyboard Shortcuts

Global

| Key | Action | |---|---| | q | Quit (confirm: "Stop all tunnels and exit? Y/n") | | a | Add new tunnel (opens wizard modal) | | ? | Full help overlay (dismissible with any key) | | Tab | Switch focus: sidebar / main panel | | 1 2 3 | Switch main panel tab (Details / Logs / Metrics) | | Up / Down / k / j | Navigate tunnel list | | c | Copy public URL to clipboard | | o | Open public URL in browser |

Sidebar Focused

| Key | Action | |---|---| | e | Edit selected tunnel | | d | Delete selected tunnel (with confirmation) | | s | Start/stop selected tunnel | | r | Restart selected tunnel |

Modal Active

| Key | Action | |---|---| | Tab | Next field | | Enter | Confirm / Submit | | Esc | Cancel / Close modal | | Up / Down | Navigate select lists |

How It Works

tuinnel uses a hybrid approach:

  1. Cloudflare REST API handles all CRUD operations — creating tunnels, managing DNS CNAME records, and configuring ingress rules
  2. cloudflared binary runs as a connector process that maintains the actual tunnel connection to Cloudflare's edge network
  3. Prometheus metrics are scraped from cloudflared's local metrics server for real-time dashboard stats (request counts, latency percentiles, connection health)

When you run tuinnel 3000:

  1. Opens the TUI dashboard
  2. Creates a named tunnel on Cloudflare (or reuses an existing one)
  3. Configures the tunnel's ingress rules to route traffic to localhost:3000
  4. Creates a DNS CNAME record pointing your subdomain to the tunnel
  5. Spawns a cloudflared connector process with live metrics and logs in the dashboard

Tunnels are named with a tuinnel- prefix on Cloudflare to avoid collisions with other tools.

Config File

Stored at ~/.tuinnel/config.json:

{
  "version": 1,
  "apiToken": "your-api-token",
  "defaultZone": "mysite.com",
  "tunnels": {
    "app": {
      "port": 3000,
      "subdomain": "app",
      "zone": "mysite.com",
      "protocol": "http",
      "lastState": "running",
      "tunnelId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    },
    "api": {
      "port": 8080,
      "subdomain": "api",
      "zone": "mysite.com",
      "protocol": "http",
      "lastState": "stopped"
    }
  }
}

The lastState field tracks whether each tunnel was running or stopped when you last exited. Tunnels with lastState: "running" auto-start when you open the dashboard. The tunnelId field caches the Cloudflare tunnel UUID for faster restarts.

The cloudflared binary is managed at ~/.tuinnel/bin/cloudflared.

Development

# Install dependencies
bun install

# Build
bun run build

# Build in watch mode
bun run dev

# Type-check
bunx tsc --noEmit

# Run tests
bun test

# Run a single test file
bun test tests/cloudflare/api.test.ts

# Smoke test the built CLI
node dist/index.js --help

License

MIT