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

@socket.dog/qtunnel

v0.4.1

Published

Programmatic tunnels from Node.js/TypeScript plus the qt tunneling CLI

Readme

@socket.dog/qtunnel

Quick tunnels to *.socket.now — from the command line or programmatically from Node.js/TypeScript applications.

Install

npm install @socket.dog/qtunnel
# or
bun add @socket.dog/qtunnel

The right binary for your platform is installed automatically via optional dependencies (linux-x64, darwin-arm64, darwin-x64, win32-x64).

Note: the library is ESM-only — use import to load it. The bundled qt/qts CLI binaries work everywhere as usual.

CLI

npx qt 3000              # expose localhost:3000
npx qt localhost:5432    # expose a specific host:port

Library usage

import { createTunnel } from "@socket.dog/qtunnel";

const tunnel = await createTunnel(3000);
console.log(tunnel.urls[0]);
// => https://knowledgeable-amicable-wizard.socket.now

// ...your app serves traffic on port 3000...

await tunnel.close();

Tunnels are backed by the bundled qt client and are tied to your app's lifetime: if your process exits (or is killed with SIGINT/SIGTERM) without you calling close(), all active tunnels are torn down automatically.

Examples

Expose multiple targets:

const tunnel = await createTunnel({ target: [3000, "localhost:5432"] });
for (const e of tunnel.entries) {
  console.log(`${e.url} -> ${e.target}`);
}

Wait for readiness explicitly / handle reassignments:

const tunnel = await createTunnel({ target: 8080 });

tunnel.on("update", (urls) => console.log("tunnels:", urls));
tunnel.on("close", ({ code }) => console.log("client exited", code));

// or without awaiting createTunnel's built-in readiness wait:
tunnel.whenReady.then(() => console.log("ready:", tunnel.urls));

Use with an Express/Next/etc. server:

import { createTunnel } from "@socket.dog/qtunnel";
import express from "express";

const app = express();
app.get("/", (_req, res) => res.send("hi"));

app.listen(3000, async () => {
  const tunnel = await createTunnel(3000);
  console.log("public url:", tunnel.urls[0]);
});

API

createTunnel(input): Promise<Tunnel>

Creates a tunnel and resolves once public URLs have been assigned.

input is either an options object or just the target(s) — createTunnel(3000) and createTunnel({ target: 3000 }) are equivalent.

| Option | Type | Default | Description | | -------------- | ------------------------------------- | ------------------------ | ---------------------------------------------------------------------- | | target | string \| number \| Array | required | Port (3000), address (localhost:3000, :3000) or a list of these | | control | string | $QT_CONTROL_ADDR | Control server address override; when unset the config file's control: value (or control.socket.now:443) applies | | tcp | boolean | false | Force TCP transport instead of QUIC | | verbose | boolean | false | Verbose logging from the underlying client | | config | string | $QT_CONFIG_FILE | Path to a qt.yaml config file | | initIdentity | boolean | true | Run qt init automatically if no identity exists yet | | cwd | string | process.cwd() | Working directory for the underlying client | | env | object | – | Extra environment variables | | timeoutMs | number | 30000 | How long to wait for URL assignment before throwing (0 disables) | | autoCleanup | boolean | true | Close the tunnel when the process exits | | binaryPath | string | bundled binary | Override the path to the qt executable |

Throws if the client exits early (e.g. bad identity), cannot be launched, or no URLs are assigned within timeoutMs.

class Tunnel extends EventEmitter

  • urls: string[] — current public URLs, e.g. ["https://x.socket.now"]
  • entries: {url, target}[] — URL/local-target pairs
  • hostnames: string[] — assigned hostnames
  • targets: readonly string[] — normalized local targets
  • pid: number | undefined — pid of the underlying client
  • ready: boolean, closed: boolean
  • whenReady: Promise<void> — resolves on first assignment, rejects on failure
  • process: ChildProcess — the spawned client
  • close(): Promise<void> — idempotent; concurrent calls share one teardown and resolve once the client has exited

Events:

  • "ready" — first URL assignment
  • "update" — (urls: string[]) assignments changed after a reconnect
  • "stderr" — (chunk: string) diagnostic output from the client
  • "close" — ({code, signal}) the underlying client exited

Helpers

  • getBinaryPath() — resolved location of the bundled qt binary (override with the QT_BINARY environment variable)
  • normalizeTarget(target) — 3000 → "localhost:3000"

Notes

  • Identity: the first run creates a local keypair via qt init (pass initIdentity: false to manage this yourself). Tunnels are stable per identity + target.
  • Multiple targets: pass them in one call to share a single connection. If you need a specific domain for a specific target, use one createTunnel() call per target.
  • App lifetime: once ready, the tunnel never keeps your event loop alive; when your app exits, exit hooks terminate the client (a graceful SIGTERM, which qt handles by closing its connection cleanly). If your app installs no signal handlers of its own, auto-cleanup closes all tunnels on SIGINT/SIGTERM/SIGHUP and the process terminates with the conventional 128+N status code (a second signal force-exits). If your app does install handlers, the library still tears the tunnels down on those signals but leaves the exit decision to your handlers. The library's own handlers (and the exit hook) are removed once no tunnels are active, so default signal behaviour is restored. Set autoCleanup: false to opt out entirely and call close() yourself.

License

See the repository for license information.