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

puente

v0.3.0

Published

Centralized, self-hosted manager for Cloudflare Tunnels across your machines — expose local ports on your own subdomains, orchestrate remote nodes over SSH.

Readme

A centralized, self-hosted control panel for Cloudflare Tunnels across all your machines.

Run it on one computer, then expose local ports — on that machine or on any remote server you reach over SSH — on your own subdomains. No open firewall ports, no public IP, no reverse proxy to babysit.

port 5006 on this PC              →  puente.example.com
port 6060 on this PC              →  grafana.example.com
port 7008 on a remote host (SSH)  →  vw.example.com

Everything is driven from one beautiful web UI. puente talks to the Cloudflare API to create the tunnels + DNS, and orchestrates the cloudflared connector on each machine (locally or over SSH) for you.


Install & run

You need Node.js ≥ 22. Pick your package manager:

# Run once, without installing
npx puente

# Global install (recommended)
npm  install -g puente      # then:  puente
pnpm add     -g puente      # then:  pnpm approve-builds -g  &&  puente
bun  add     -g puente      # then:  puente

pnpm users: since pnpm 10, dependency build scripts are blocked by default, so pnpm asks to approve the native deps (better-sqlite3, ssh2, cpu-features). Approve them once after installing with pnpm approve-builds -g, or keep a one-off run non-interactive with pnpm --allow-build=better-sqlite3,ssh2,cpu-features dlx puente. npx and bun build them automatically with no prompt.

The scoped name @pauldvlp/puente is an identical alias published at the same version — npx @pauldvlp/puente works too if you prefer it.

Then just run:

puente            # starts the panel on http://localhost:5006 and opens your browser
puente setup      # same, and drops you straight into the guided setup

That's it. The first screen walks you through everything.

The panel keeps running in the background and gives your terminal back, so closing it (or pressing Ctrl+C) no longer takes the tunnels down. Manage it with puente status, puente logs -f, puente restart and puente stop. Need it attached to the terminal instead — a container, a systemd unit, CI? Add --foreground.

…or with Docker

# from a clone of the repo
docker compose up -d          # panel on http://localhost:5006, state in a named volume

State persists in the puente-data volume (mounted at /data). Override the port with the PUENTE_PORT environment variable.


First-run setup (about 2 minutes)

  1. Create your admin account — a single local login for the panel.
  2. Connect Cloudflare — paste a scoped API token (see below). puente verifies it, shows your domains, and stores it encrypted on disk.
  3. Add a node — “This machine”, or a remote host over SSH.
  4. Provision it — puente installs cloudflared, creates the tunnel, and starts the connector.
  5. Publish a route — map a local port to a subdomain. Live in seconds.

Cloudflare API token — exact permissions

Create the token at dash.cloudflare.com → My Profile → API TokensCreate Token → Create Custom Token, and add exactly these four permissions:

| Category | Permission group | Access | Why | | ----------- | ----------------- | -------- | ------------------------------------------------------ | | Account | Cloudflare Tunnel | Edit | Create & configure tunnels and their ingress rules | | Zone | DNS | Edit | Create the proxied CNAME records that route subdomains | | Zone | Zone | Read | List your domains so you can pick where to publish | | Account | Account Settings | Read | Auto-discover your account id |

Then set Account Resources = your account and Zone Resources = All zones (or the specific domains you want to manage), create the token, and copy it once. The panel shows this same checklist while you set up.

Prefer not to use a token? You can also authorize cloudflared with the browser login flow (cloudflared tunnel login) — but a scoped API token is the recommended, least-privilege path and the only one puente can fully automate.


Managing remote machines over SSH

Add a node of type Remote (SSH) with its host, port and user. puente can reuse aliases from your ~/.ssh/config.

No key set up yet? Use “Passwordless SSH” on the node. Enter the host password once; puente generates a dedicated ed25519 key, installs it in the remote ~/.ssh/authorized_keys with correct permissions, verifies key auth works, and never stores the password. From then on, everything is key-based.

When you provision a remote node, puente (over SSH):

  • detects the OS/architecture (uname),
  • installs cloudflared if it's missing (correct binary for that platform),
  • installs it as a persistent service (cloudflared service install, via sudo -n) so it survives reboots — or falls back to a background process if passwordless sudo isn't available,
  • wires up the tunnel token and ingress.

CLI

puente [start]           # start the panel in the background (default command)
                         #   --port <n>, --host <h>, --no-open, --foreground
puente setup             # start + open the guided setup wizard
puente stop              # stop the background panel (SIGTERM, then SIGKILL)
puente restart           # stop + start, reusing the port/host it was on
puente status            # running? pid, URL, uptime — exits 3 when stopped
puente logs [-n 50] [-f] # show (or follow) the background log
puente doctor            # check the local environment (Node, cloudflared, ssh)
puente info              # print paths and version

A background panel records its pid and URL in ~/.puente/daemon.json and writes its output to ~/.puente/puente.log (rotated to puente.log.1 past 5 MB). A second puente start detects the running one instead of fighting it for the port; a state file left behind by a crash is cleaned up automatically.

Environment variables: PUENTE_PORT (default 5006), PUENTE_DATA_DIR (default ~/.puente).


Concepts

  • Node — a machine that runs a cloudflared connector. One node = one Cloudflare Tunnel. The machine running puente is the local node; everything else is an ssh node.
  • Route — a mapping from a subdomain (e.g. vw.example.com) to a local service on a node (e.g. http://localhost:7008). Publishing a route updates the tunnel's ingress rules and creates the proxied DNS record.

Alerts (Pro)

A webhook channel receives a POST with this body — versioned and safe to parse:

{
  "v": 1,
  "trigger": "node.down",
  "text": "Node vps-fra is down (was healthy)",
  "severity": "critical",
  "subject": { "kind": "node", "id": "node_...", "name": "vps-fra" },
  "from": "healthy",
  "to": "down",
  "at": "2026-08-20T22:31:00.000Z"
}

Slack and Discord channels get the shape each of them expects instead ({ text } and { content }). Recoveries are sent as well as failures, and a 10-minute cooldown per subject keeps a flapping tunnel from turning into a thousand messages — recoveries ignore the cooldown, because a late all-clear is useless. Nothing fires for inactive: that is what stopping a connector on purpose looks like, and paging someone for doing what they asked is how alerting gets muted.

Data & security

  • All state lives in ~/.puente/ (SQLite database, encryption key, managed SSH keys).
  • Secrets (Cloudflare token, tunnel tokens) are encrypted at rest with AES-256-GCM; the 256-bit key is generated on first run and stored chmod 600.
  • The admin password is hashed with scrypt. Sessions use signed JWTs.
  • Nothing is sent anywhere except the Cloudflare API. There is no telemetry.

How it works

             ┌──────────────── your browser ────────────────┐
             │      React + Vite dashboard (this UI)         │
             └───────────────────┬──────────────────────────┘
                                 │  REST + SSE (live status)
             ┌───────────────────▼──────────────────────────┐
             │   puente control plane  (NestJS + SQLite)      │
             │   • Cloudflare API (tunnels, ingress, DNS)    │
             │   • SSH orchestration (node-ssh)              │
             │   • cloudflared lifecycle (local & remote)    │
             └───────┬───────────────────────────┬──────────┘
              local  │ child_process         SSH  │
             ┌───────▼───────┐             ┌──────▼───────────┐
             │  cloudflared  │             │   cloudflared    │
             │  (this PC)    │             │   (remote node)  │
             └───────────────┘             └──────────────────┘
                     ╲                              ╱
                      ╲     Cloudflare edge        ╱
                       ▼   (your subdomains)      ▼

Tunnels are remotely-managed (config_src: cloudflare): configuration lives in Cloudflare, and each connector only needs its run token — so puente can reconfigure routes at any time via the API, from one place.


Requirements

  • Node.js ≥ 22 on the machine running puente.
  • A Cloudflare account with at least one domain (zone).
  • For remote nodes: SSH access. cloudflared is installed automatically if missing.
  • better-sqlite3 ships prebuilt binaries; on unusual platforms a C/C++ toolchain + Python may be needed to build it.

Contributing & security

puente is open source (AGPL-3.0), with a commercially-licensed ee/ directory for the Pro edition — see LICENSING.md. Contributions are welcome — see CONTRIBUTING.md. Found a vulnerability? Please report it privately per our Security Policy.

Source & issues: https://github.com/pauldvlp/puente


AGPL-3.0-or-later © Paul Barahona