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

devroulette-cli

v0.1.33

Published

Chatroulette for devs waiting on long Claude Code tasks.

Readme

DevRoulette

Chatroulette for devs waiting on Claude Code. A long-running task opens a chat and pairs you with a random dev who's also around. From there it's yours — chat as long as you like, /skip for a new dev, /quit to close. It's a terminal chat (and a VS Code sidebar — see vscode/).

Anonymous. No accounts. No names you pick. Chat not logged.

18+ only — you're chatting with random, anonymous strangers, at your own risk. By using DevRoulette you agree to the Terms & Privacy.

Not using Claude Code? Open a chat anytime, in any terminal, with devroulette start.


Install (30 seconds)

npm i -g devroulette-cli
devroulette init

That's it. When a Claude Code task runs longer than 30 seconds, DevRoulette quietly looks for another dev who's also around — in the background, with no window. A chat window only pops the instant you're actually matched. If nobody's around before your task ends, nothing ever opened and you never saw a thing — you only see the app when it's working. There's one window per machine; a later task won't stack a second one.

The task is just the trigger: once the window is open the chat is yours and is not tied to the task — it stays until you leave. /skip for a new dev, /report to flag + skip, /insights for room stats, /quit to close. Partner leaves → you're matched with someone new. Closed it? Start another long task to reopen.

devroulette init merges two hooks into ~/.claude/settings.json (UserPromptSubmit + Stop). It backs the file up first and never touches your existing hooks. Remove them anytime with devroulette uninstall.


Run your own server (full VPS deploy)

The matchmaking server is stateless and tiny — a $5 VPS is plenty. It listens on plain ws:// bound to localhost; Caddy terminates TLS in front of it so clients connect over wss://.

1. Build & place the server

git clone <this repo> /opt/devroulette && cd /opt/devroulette
npm ci
# transpile or run with tsx; example uses tsx:

2. Run as a non-root user via systemd

Create a dedicated user and a unit file at /etc/systemd/system/devroulette.service:

[Unit]
Description=DevRoulette matchmaking server
After=network.target

[Service]
Type=simple
User=devroulette
Group=devroulette
WorkingDirectory=/opt/devroulette
Environment=HOST=127.0.0.1
Environment=PORT=8787
# Trusted reverse-proxy hops in front of the server. Caddy-only = 1. If a CDN /
# platform edge (Cloudflare, Railway, Fly…) also sits in front, set the TOTAL hop
# count (e.g. 2). Per-IP limits and bans key on the resulting client IP — leave it
# at the default 0 and they key on the proxy's IP (everyone shares one bucket).
Environment=TRUSTED_PROXY_HOPS=1
ExecStart=/usr/bin/npx tsx server/src/index.ts
Restart=always
RestartSec=2
# hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=
CapabilityBoundingSet=
[Install]
WantedBy=multi-user.target
sudo useradd --system --no-create-home --shell /usr/sbin/nologin devroulette
sudo systemctl daemon-reload
sudo systemctl enable --now devroulette

3. Auto-TLS with Caddy

/etc/caddy/Caddyfile:

chat.example.com {
    reverse_proxy 127.0.0.1:8787
}

Caddy fetches and renews the certificate automatically and proxies the WebSocket upgrade. It appends the real client IP to X-Forwarded-For; the server reads the trusted hop from the right (never the spoofable leftmost value) per TRUSTED_PROXY_HOPS. Set Caddy's trusted_proxies so the forwarded header is sanitised, and make sure TRUSTED_PROXY_HOPS matches your real proxy depth.

Never expose the server without a proxy. It binds 127.0.0.1 by default and the npm start script no longer forces 0.0.0.0. With TRUSTED_PROXY_HOPS=0 (the default) it trusts only the direct socket peer, so per-IP limits stay sound even if someone points it straight at the internet — but TLS and real rate-limiting still belong in a proxy in front.

Clients then use:

DEVROULETTE_URL=wss://chat.example.com devroulette init

(A bare devroulette init bakes the public production server by default. Pass a URL — devroulette init ws://127.0.0.1:8787 — or set DEVROULETTE_URL to point at your own server / local dev.)


Develop

npm install
npm run typecheck   # tsc (server+shared) + tsc (cli/jsx)
npm run lint        # eslint
npm test            # integration tests
npm run server      # run the server locally on :8787
npm run devroulette -- --debug   # join manually (testing only)

Threat model — what is and isn't protected

Protected

  • Terminal injection. All inbound text is stripped of ANSI escape sequences and C0/C1 control characters server-side before relay and again client-side before render. Received text is rendered as plain text only — no markdown, no clickable links, no emoji-shortcode expansion. Received content is never written to disk and never executed.
  • Spam / flooding. Per-socket 1 msg/sec, 500 chars/msg, 200 msgs/room, 16 KB frame cap, min 0.5s between skips. Per-IP (keyed on the real client IP via TRUSTED_PROXY_HOPS): max 3 concurrent sockets, max 5 queue-joins/min, repeated violations → 15-minute in-memory temp-ban (counter resets when a ban is issued). A global socket ceiling (MAX_TOTAL_SOCKETS) bounds total load regardless of IP.
  • Queue eclipse. Each session/match-key may hold only ONE queue slot, so an attacker can't park a cluster of same-key sockets to capture every real joiner.
  • Coerced bans. A report removes the reporter from the room but never bans the partner — anonymous, spoofable reports must not drive bans.
  • Malformed input. Every inbound frame is validated against a strict zod schema; anything else is dropped and counts as a violation.
  • Persistence leaks. Message content, rooms, IPs, and handles are never written to disk — they live only in memory and rooms are destroyed on disconnect / task_done / 10-min idle. The server persists only aggregate usage counters (connections / convos / messages) so stats survive restarts — never any per-device identifier, IP, or message content.
  • Resource exhaustion. Heartbeat ping/pong kills dead sockets within ~30s.
  • Bots / connect-spam. Every socket must solve a small sha256 proof-of-work (~16 leading zero bits) before it can join the queue — silent and instant for a real client, but it puts a CPU cost on mass automated connects. A socket that never solves the challenge is dropped after POW_DEADLINE_MS (10s) instead of squatting. Per-IP socket and rate limits still apply on top.
  • Liveness / task gate. A client only counts as "waiting" while its Claude Code task transcript keeps growing. The client checks the transcript by file size / mtime only — it never opens, reads, or transmits the file, so no code or prompt data leaves the machine. It emits a heartbeat while the task is live; the server ejects any connection that misses ~2 beats. No live task = no entry, no staying.
  • Debug lockout. The hidden manual-join mode only activates locally with DEVROULETTE_DEV=1, and the server rejects debug connections outright unless the operator sets DEVROULETTE_ALLOW_DEBUG=1. Production keeps it off.
  • Hook safety. The installer backs up settings.json, never clobbers existing hooks, parses hook JSON in Node (never interpolated into a shell command). The npm package has no postinstall scripts and pinned dependencies.
  • Zero AI/API calls. The app never calls any model or API — it has no HTTP client at all. Its only network egress is the WebSocket to the matchmaking server. It cannot touch your Claude quota.

NOT protected (by design / out of scope)

  • Social engineering. A human partner can still type a phishing link or lie. We render it inert (not clickable) but cannot stop a person from being malicious.
  • Anonymity vs. the server operator. The server sees IP addresses in memory (used for rate-limiting) and could be modified to log them. Run your own if that matters. The per-session match key is a salted sha256 of the Claude session id (not the raw id), so the operator can't tie it back to your on-disk transcript filename — but a hostile operator still sees your IP and the plaintext it relays.
  • Entry gating is best-effort. Public entry requires the hook trigger plus a live, growing task transcript (the liveness gate). This strongly biases the queue toward people genuinely waiting — but it watches fs metadata, so a determined user could forge a growing file. It is not a cryptographic proof of a task.
  • No end-to-end encryption. TLS protects transport to the server; the server relays plaintext in memory. Not for sensitive content.
  • DoS. Per-IP limits raise the cost of abuse but a distributed flood can still exhaust a $5 VPS. Put Cloudflare / a real rate-limiter in front for production.

Legal

DevRoulette is 18+ only and provided as-is, with no warranty. You chat with random anonymous strangers; we're not responsible for what other users say or do. Messages are never stored or logged; the server processes IP addresses in memory only, for rate-limiting and bans. Full terms and the privacy disclosure:

Terms & Privacy