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

@iamken/cloudtunnel

v0.1.5

Published

Manage Cloudflare Tunnels and subdomains account-wide from the CLI — instant HTTPS sharing on your own domains.

Downloads

820

Readme

⛅ cloudtunnel

Expose any local port at a public HTTPS subdomain — on your own Cloudflare domains.

Instant, self-owned tunnel sharing — the tunnel and DNS live in your Cloudflare account, so you keep clean, stable URLs and full control.

npm version CI license node

npm i -g @iamken/cloudtunnel

cloudtunnel login     # once — paste a Cloudflare token; account + domain auto-resolved
cloudtunnel 3000      # → https://brave-otter-1a2b.example.com is live ✨

💨 Prefer less typing? ctun is a built-in short alias — ctun 3000, ctun ls, ctun down 1.


✨ Why cloudtunnel

  • 🔗 Your domains, real subdomains — routes through native Cloudflare Tunnel to *.your-domain.com, not a shared third-party host.
  • One commandcloudtunnel 3000 creates the tunnel, DNS, and connector, then prints a live HTTPS URL. It even asks you which domain + subdomain to use.
  • 🧭 Just two statesup brings a subdomain online; down (or Ctrl-C) releases it (deletes the tunnel + DNS). Re-running up always starts clean — no leftovers, no conflicts.
  • 🗂️ Profiles — save a whole project's services and bring them all up with cloudtunnel run mb.
  • 🌙 Background mode--detach keeps connectors running after you close the terminal.
  • 🔒 Secure by default — token passed via env (never argv), stored 0600, destructive ops are ownership-gated and re-verified.

🚀 Quickstart

cloudtunnel login                 # authenticate once
cloudtunnel 3000                  # asks for domain + subdomain, then goes live
cloudtunnel 3000 -s api           # skip the prompts: api.<your-domain>
cloudtunnel 3000 -s api -d foo.io # subdomain + domain
cloudtunnel 3000 -s @             # the root domain itself (example.com)
cloudtunnel 3000 --detach         # run in the background

Replacing an existing DNS record asks for confirmation first — pass -y to skip, or -f to also replace a non-tunnel record.

Run cloudtunnel 3000 with no flags and it guides you:

┌  cloudtunnel
◇  Choose a domain
│  ● example.com   ○ foo.io
◇  Subdomain
│  api            (leave blank for a random name)
◇  Connected
│
◇  Live ─────────────────────────────────────────╮
│  https://api.example.com  →  http://localhost:3000
│  Ctrl-C stops and releases this subdomain
╰─────────────────────────────────────────────────╯

📦 Commands

| Command | What it does | | --- | --- | | cloudtunnel login | Authenticate; resolve account + list your domains. --status to inspect. | | cloudtunnel <port> · up | Bring a subdomain online. -s/--subdomain, -d/--domain, --detach, -f/--force, --proto. | | cloudtunnel ls · ps | List subdomains — # · SUBDOMAIN · TARGET · STATE · PID. --all scans the whole account. | | cloudtunnel down <target> · rm · stop | Release a subdomain — stop connector + delete tunnel + DNS. --all, --dry-run, -f. | | cloudtunnel logs <target> | Show a connector's log. -f to follow, -n for line count. | | cloudtunnel zones | List the domains in your account. | | cloudtunnel save <profile> <svc…> | Save a group of services. svc = name:port[:proto], or --from-running. | | cloudtunnel run <profile> [--detach] | Bring up every service in a profile at once. | | cloudtunnel profiles [--rm <name>] | List saved profiles (or delete one). |

A <target> is a # number, a subdomain name, a full hostname, or a tunnel-id prefix — all shown in ls. down also accepts rm / remove / delete / stop.

$ cloudtunnel ls
┌───┬─────────────────────┬────────────────────────┬───────┬───────┐
│ # │ SUBDOMAIN           │ TARGET                 │ STATE │ PID   │
├───┼─────────────────────┼────────────────────────┼───────┼───────┤
│ 1 │ api.example.com     │ http://localhost:3000  │ up    │ 48213 │
│ 2 │ web.example.com     │ https://localhost:5173 │ down  │ -     │
└───┴─────────────────────┴────────────────────────┴───────┴───────┘

$ cloudtunnel down 1        # release by number
$ cloudtunnel down --all    # release everything

🗂️ Profiles

Expose a whole project's services with one command:

cloudtunnel save mb api:3000 web:5173:https   # define the group (or: save mb --from-running)
cloudtunnel run mb --detach                    # backend + frontend live in the background
cloudtunnel logs api -f                         # follow one service's log
cloudtunnel down --all                          # release them all

🧭 Two states: up & down

There are only two states. up brings a subdomain online (creating the tunnel + DNS). down — or pressing Ctrl-C in a foreground upreleases it: it stops the connector and deletes the tunnel + DNS on Cloudflare. Running up again recreates it cleanly (any leftover tunnel record for that name is cleaned up first, so you never hit conflicts).

Add --detach to keep a connector running in the background after the CLI exits (and after you close the terminal). Release it later with cloudtunnel down <target> (or --all) and tail its output with cloudtunnel logs <target> -f.


🔑 Authentication

Create a Cloudflare API token with these scopes (least-privilege — a token limited to the domains you tunnel into is recommended):

| Resource | Permission | | --- | --- | | Account | Cloudflare Tunnel · Edit | | Account | Account Settings · Read | | Zone | DNS · Edit | | Zone | Zone · Read |

Provide it via (highest precedence first): CLOUDFLARE_API_TOKEN env → cloudtunnel login --token-stdin → the hidden prompt from cloudtunnel login.

🔒 The token is never passed on the command line (that would leak into ps / shell history) and is stored 0600. Rotate it if a shared host is compromised.


✅ Requirements

  • Node.js ≥ 20
  • cloudflared on your PATH — install via brew install cloudflared, your package manager, or the releases page. (Auto-download is built in and fetches a pinned, SHA256-verified binary, but stays disabled until release checksums are pinned — treat cloudflared as a prerequisite for now.)

🩺 Troubleshooting

| Symptom | Cause & fix | | --- | --- | | HTTP 1016 / subdomain won't load | The connector isn't running (STATE = down in ls). Bring it back up: cloudtunnel <port> -s <name>. | | down says "active connections" | Handled automatically — cloudtunnel cleans up the connections and retries the delete. | | "grey-clouded" error | The zone couldn't proxy the record; cfargotunnel routing needs an orange-cloud (proxied) CNAME. | | A DNS record already occupies the name | Pick another -s/-d, or pass -f/--force to replace a non-tunnel record. |


📄 License

MIT © thanhken