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

talklan

v0.1.0

Published

Temporary local-network communication room. Start a server, scan the QR, talk. No cloud, no account, no internet required.

Readme

TalkLAN

Temporary local-network chat rooms. Start a server, scan the QR code with a phone, talk. No account, no app install, no cloud, no internet required.

Why

Sharing a quick conversation or a file with people on the same Wi-Fi should not require accounts, hosted services, or uploading data to someone else's server. TalkLAN runs one ephemeral room on your machine; everything lives in memory and disappears when the process exits.

Install

npm i -g talklan
# or run without installing:
npx talklan

Requires Node.js >= 20.

Run

talklan                          # room on port 8787, expires in 30m, QR shown
talklan --room "Study group" --password s3cret --expires 1h
talklan -r "Team A" -r "Team B"  # two rooms; --password applies to all of them

Then join from a phone: scan the QR code printed in the terminal, or open http://<lan-ip>:<port> in a mobile browser to reach the lobby. Pick a room, pick a display name (and enter the password if the room has one) and chat. Attach files with 📎, tap Reply on any message to quote it, and use Invite in the header to show a QR + link for the next person. The 🔔 button toggles message sounds. New rooms can also be created from the lobby UI (optional password); up to 20 rooms per server.

Options

| Flag | Default | Description | | ------------- | ------- | -------------------------------------------------- | | --port | 8787 | Port to listen on. Default port busy? Next free port is tried automatically; an explicitly passed busy port fails with a clear error. | | --room | host-based | Display name of a startup room; repeatable (-r A -r B starts one room per flag) | | --password | none | Optional password applied to ALL startup rooms (never in URLs or logs) | | --expires | 30m | Room lifetime: 10m, 30m, 1h, or 2h. The server shuts down automatically when time runs out. | | --max-file-size | 50mb | Max upload size: bytes (1048576) or kb/mb/gb suffix (512kb, 50mb, 1gb) | | --max-users | unlimited | Max participants (integer >= 1). Extra joins get a ROOM_FULL error and can retry later. | | --history-file | none | Write history JSON ({exportedAt, rooms:[{id, room, startedAt, endedAt, messages}]}) to this path on shutdown | | --no-qr | — | Suppress the terminal QR code | | --no-browser| — | Do not open a browser on the host | | --verbose | — | Verbose logging (still never logs secrets/content) | | --help | — | Show help | | --version | — | Show version |

Stop with Ctrl+C: the server stops accepting connections, notifies clients, closes sockets, and deletes room state plus temp files. (With --history-file, history JSON is written first.)

Replies

Send {type:"chat", content, replyTo:"<message-id>"} (or upload with ?senderId=…&roomId=…&replyTo=…) to quote another message. The server snapshots the quote ({id, senderName, text} — chat text sliced to 140 chars, files as File: <name>); client-supplied quote content is never trusted, and unknown replyTo ids are silently ignored.

Lobby HTTP API

  • GET /api/rooms → {rooms: [{id, name, hasPassword, participantCount, messageCount}]}
  • POST /api/rooms ({name, password?}) → 201 {id, name, hasPassword}; bad names → 400 INVALID_NAME, more than 20 rooms → 403 ROOM_LIMIT
  • GET /api/rooms/:id → {id, room, expiresAt, hasPassword, participantCount, maxUsers, messageCount}; unknown id → 404 UNKNOWN_ROOM
  • GET /api/qr[?room=<id>] → SVG QR of the lobby (or one room's) join URL, image/svg+xml, no-store; unknown id → 404 UNKNOWN_ROOM
  • WebSocket per room at /ws/:id; join with {type:"join", name, password?} (no tokens in URLs anymore). Unknown room → {type:"error", code:"UNKNOWN_ROOM"} then close 1008.

Security model

  • LAN-only. No outbound requests, no telemetry, no analytics.
  • Temporary by default. Room state, messages, uploads, and sessions live in memory (Map<string, Room/Client/Session>) and are gone on exit.
  • Central relay. client → server → client(s) over WebSocket. No WebRTC peer-to-peer.
  • Server is authoritative. IDs, timestamps, and presence come from the server; every WebSocket message is validated and malformed input gets an error reply, never a crash. Rate limits stop one device freezing the room.
  • Secrets stay secret. Passwords are never in URLs or logs. Tokens are crypto.randomBytes-grade, expire with the room, and are never logged.
  • XSS-safe rendering. The web client uses textContent only, never innerHTML, for user content; usernames cannot contain HTML or spoof system.
  • Safe files. Uploads use generated internal IDs, sanitized names, size limits, and streaming (no full-file buffering). Path traversal (../, absolute paths, symlink escapes) is rejected. Temp files are deleted on room end/expiry.

Dev commands

npm install
npm run dev        # local server (tsx)
npm run build      # compile server (dist/) + web client (web/public/js/)
npm run test       # unit + integration + security tests
npm run lint       # typecheck both projects + forbid innerHTML
npm run typecheck  # tsc --noEmit for server and web projects
npm run format     # check formatting (node scripts/format.mjs --fix to fix)

Architecture

 phone browser ──┐
                 │  HTTP (static UI + /api/*) / WebSocket (chat relay)
 laptop browser ─┼──▶  talklan server (Node.js, in-memory room) ──▶ LAN IP + QR
 phone browser ──┘
        ▲                         │
        └──── file download ── upload ── chat/presence/typing ──┘
  • src/cli/ — args, terminal UI
  • src/server/ — HTTP server, WebSocket relay, rooms, clients, router
  • src/network/ — LAN interface/IP detection, QR
  • src/messages/ — protocol types, validation, handlers
  • src/files/ — upload/download relay, filename security
  • src/auth/ — room token, sessions
  • src/config/ — defaults
  • web/ — static index.html plus TypeScript client (app.ts, websocket.ts, state.ts, api.ts), compiled with tsconfig.web.json (no bundler, no npm dependencies for the web client)
  • tests/unit|integration|security — unit, integration, and security tests (XSS, traversal, malformed WS, oversized message/upload, bad token, flood, malicious filename)
  • scripts/ — lint/format helpers

The WebSocket protocol is versionable. Client → server: join, chat, typing, ping. Server → client: joined, chat, file, presence, typing, system, error, shutdown, pong.

No-cloud statement

TalkLAN works fully offline on a local network. It does not use Firebase, Supabase, hosted databases, external auth, or any third-party service, and it sends no data anywhere except between devices on your LAN.

License

MIT — see LICENSE.