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

planche

v0.8.11

Published

Local agent for Planche — lets the browser-based Planche SQL client reach MySQL/MariaDB/PostgreSQL/SQLite/SQL Server/Oracle/Neon/Supabase/Cloudflare D1 databases and local Durable Object storage from your machine.

Readme

planche

Local agent for Planche, a browser-based SQL client for MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, Oracle, Neon, Supabase and Cloudflare D1.

Browsers cannot open TCP connections to a database, so Planche runs this small agent on your machine. The app in your browser talks to the agent over 127.0.0.1, and the agent talks to your databases. Credentials and query results stay on your computer — nothing is sent to planche.dev.

Quick start

With Node.js 22 or newer:

npx planche

Or install it — no Node.js required, and it starts instantly afterwards:

curl -fsSL https://planche.dev/install.sh | sh     # macOS, Linux
irm https://planche.dev/install.ps1 | iex          # Windows

The installer puts everything under your home directory (never asks for root), uses the Node.js already on the machine when it is 22 or newer and downloads a private one otherwise, and leaves a planche command on your PATH. It then starts the agent for you, since a shell only picks up a new PATH in a new terminal (PLANCHE_NO_START=1 installs without starting). Update it with planche upgrade, remove it with planche uninstall — your settings in ~/.planche are kept either way.

The agent starts on port 8888, prints a pairing link and opens it in your default browser:

https://planche.dev/#agent=http://127.0.0.1:8888&token=…

Opening that link pairs the app with the agent. From then on you can add connections in the app as usual; every connection is opened by the agent on your machine.

The token is random on every start. If you restart the agent, the app shows "agent restarted" and asks you to open the new link (or set a fixed token, see below).

Usage

npx planche [options]

  -p, --port <n>       Port to listen on (default: 8888)
      --addr <host>    Address to bind (default: 127.0.0.1)
      --token <str>    Auth token (default: random on each start)
      --print-token    Print the token (hidden by default; the pairing link is enough)
      --no-open        Print the pairing link without opening a browser
      --verbose        Log every request

planche upgrade [version]   Update an installed copy (not needed with npx)
planche uninstall [--yes]   Remove it again, keeping ~/.planche
  -h, --help           Show help

Every option can also be set through an environment variable:

| Variable | Option | Notes | | ------------------------- | ---------- | --------------------------------------------------- | | PLANCHE_PORT | --port | | | PLANCHE_ADDR | --addr | | | PLANCHE_AUTH_TOKEN | --token | Fixed token (16+ characters); the app stays paired across restarts | | PLANCHE_BODY_LIMIT | | Max request body, default 50mb (large SQL scripts) | | PLANCHE_SQLITE_DIR | | Only SQLite files inside this directory may be opened | | PLANCHE_SSH_CONFIG | ~/.ssh/config | SSH client config used to resolve jump-host aliases; none disables reading it and ~/.ssh/id_* |

Examples

Keep the same token between restarts so the app never needs re-pairing:

PLANCHE_AUTH_TOKEN=$(openssl rand -base64 24) npx planche   # at least 16 characters

Run on another port and print the link only:

npx planche --port 9000 --no-open

Restrict SQLite access to one folder:

PLANCHE_SQLITE_DIR=~/databases npx planche

How it works

The agent is a small HTTP server with a handful of JSON endpoints (/connect, /query, /disconnect, /test-connection, /health). The app calls them with the bearer token from the pairing link; the agent keeps the database connections open and forwards queries with the matching driver (mysql2, pg, better-sqlite3).

  • MySQL / MariaDB — switching databases uses USE, so open transactions and session variables survive.
  • PostgreSQL — one connection is bound to one database; the "database" you pick in the app is a schema of it and is applied with SET search_path.
  • SQLite — opens a file on your disk by path. By default only files under your home directory are allowed, excluding hidden directories (~/.ssh, …) and ~/Library / AppData (browser cookie stores live there); new files must have a .db / .sqlite / .sqlite3 extension, and ATTACH DATABASE is refused. Set PLANCHE_SQLITE_DIR to open files elsewhere (then only that directory is allowed), PLANCHE_SQLITE_ATTACH=1 to allow ATTACH, and PLANCHE_SQLITE_URI=1 for file: URIs. (Small SQLite files can also be opened directly in the browser without the agent.)
  • SQL Server — same model as PostgreSQL: the connection is bound to one database and the app's "database" is a schema (dbo, ...). USE works in the editor because every statement runs on the same session; scripts may contain GO lines.
  • Oracle — thin-mode driver, no Instant Client needed. Schemas (users) are the app's "databases", switched with ALTER SESSION SET CURRENT_SCHEMA. PL/SQL blocks end with a / line as in SQL*Plus.
  • SSH tunnel — MySQL, PostgreSQL, SQL Server and Oracle profiles can go through an SSH jump host (password, private key, or the local ssh-agent). The agent opens the tunnel with ssh2 on a loopback port and points the driver at it. The host may be a Host alias from ~/.ssh/config: HostName, Port, User and IdentityFile are applied like the ssh command does (ProxyJump is not supported), and with no key given ~/.ssh/id_* are tried. An unknown host key is shown in the app (type and SHA256 fingerprint) and the connection proceeds only after you accept it; accepted keys are recorded in ~/.planche/known_hosts (plain, pattern and hashed entries in ~/.ssh/known_hosts are honoured too). A changed key refuses the connection until the stale entry is removed.

When planche.dev cannot be reached, the agent serves the copy of the app it ships with (at its own address) instead of opening a browser error page, so it keeps working offline. --serve-app forces that from the start and --no-serve-app turns it off.

That makes the agent usable on networks with no way out. Only installing it needs the outside world — npm i -g planche where npm is reachable, or npm pack planche for a tarball to carry in or push to an internal registry. From then on the served page makes no requests outside the machine at all: the editor, the SQLite engine and the icons are part of the package, and there is no CDN, web font or analytics. Hosted databases (Neon, Supabase, D1), SSH to unreachable hosts and the sample database are what you lose.

Gateway mode

Run one agent on a shared host so a team can use Planche with just a browser, without anyone holding database credentials:

npx planche --gateway gateway.json --addr 0.0.0.0 --allow-remote \
  --token "$(openssl rand -base64 24)" --public-url https://sql.example.com

gateway.json lists named profiles; the credentials never leave this file:

{
  "name": "Acme staging",
  "profiles": [
    {
      "name": "Shop (read-only)",
      "driver": "mysql",
      "host": "db.internal",
      "port": 3306,
      "user": "reporting",
      "password": "…",
      "database": "shop"
    },
    {
      "name": "Shop (writable)",
      "readOnly": false,
      "driver": "postgres",
      "host": "pg.internal",
      "port": 5432,
      "user": "app",
      "password": "…",
      "database": "shop"
    }
  ]
}

name is the gateway's own name and profiles[].name is what browsers pick; every other field is what the connection form asks for, under the same names (serviceName for Oracle, file for SQLite, accountId / databaseId / apiToken for D1, and an ssh block where you need one).

A profile behind a jump host carries that ssh block; host/port are then resolved on the jump host, so they are usually loopback:

{
  "name": "Shop via bastion (read-only)",
  "driver": "mysql",
  "host": "127.0.0.1",
  "port": 3306,
  "user": "reporting",
  "password": "…",
  "database": "shop",
  "ssh": {
    "host": "bastion.example.com",
    "port": 22,
    "user": "deploy",
    "auth": "key"
  }
}

With auth: "key" and no privateKey, the agent uses the IdentityFile from this machine's ~/.ssh/config (falling back to ~/.ssh/id_*), so the key never lands in the config file; auth: "agent" uses SSH_AUTH_SOCK. A privateKey you do set is the key contents, not a path. The jump host's key must already be in a known_hosts file here, or be pinned in the profile with acceptHostKey — in gateway mode the browser sends only a profile name and cannot answer the unknown-host-key prompt.

  • Browsers only ever send { "profile": "Shop (read-only)" }; /connect bodies with credentials are refused in gateway mode.
  • Profiles are read-only by default: the agent refuses anything but a single SELECT / WITH … SELECT / SHOW / DESCRIBE / EXPLAIN per request (statement filter), and additionally puts the session in read-only mode where the engine supports it (MySQL, PostgreSQL, SQLite). Set "readOnly": false on a profile to allow writes.
  • The agent serves the web app itself at / (--serve-app, on by default in gateway mode; --app-dir serves another build) so the app talks to its own origin — the strict CSP stays intact and no --origin is needed. Put a TLS reverse proxy in front and pass its URL as --public-url; the printed pairing link is single-use, so share the token (--print-token) with teammates and let them paste it in the connect dialog's Configure box.

Security

  • Every request except GET /health and POST /pair requires the bearer token. The pairing link carries a single-use code (valid 10 minutes) that the app exchanges for the token, so the token itself never appears in a URL. Repeated wrong tokens lock authentication for 30 seconds.
  • Browser requests are accepted only from the Planche app origin (https://planche.dev). Any other Origin is refused with 403, even with a valid token, and requests whose Host header is not the loopback address are refused (DNS rebinding).
  • The agent binds to 127.0.0.1 by default, so only the browser on the same machine can reach it. It speaks plain HTTP, so binding to another address requires --allow-remote plus an explicit token, and should only be done behind a TLS reverse proxy or an SSH port forward.
  • In gateway mode credentials live only in the gateway config file on the server; browsers pick profiles by name. Read-only profiles are enforced by the agent (statement filter + engine session read-only), not by the UI.
  • The agent never stores credentials. They are passed per connection by the app (including SSH passwords and private key contents) and live in memory only while the connection is open. The only thing written to disk is the SSH host key list in ~/.planche/known_hosts.
  • For SSH tunnels the agent reads ~/.ssh/config and, when the profile has no key, the IdentityFile it names or ~/.ssh/id_* — exactly what the ssh command would do, and the key never leaves the agent. Set PLANCHE_SSH_CONFIG=none if you would rather the agent only used what the app sends.

Troubleshooting

  • The app says the agent is offline — make sure npx planche is still running and that the port in the pairing link matches. Corporate proxies or browser extensions that block localhost requests can also cause this.
  • "Agent restarted, re-pair" — the agent generated a new token. Open the new link it printed, or start it with a fixed --token.
  • 403 Forbidden — the request did not come from https://planche.dev. Open the app from the pairing link the agent printed.
  • Wrong Node version — the SQLite driver ships prebuilt binaries for Node 22+. Check with node -v, or run with npx -p node@22 planche.

License

All rights reserved. Not yet released under an open-source license.