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

@heart-it/p2p-path

v0.1.3

Published

heartIT lab #9 — what the DHT knows about you, the millisecond timeline of a connection, and an honest read of the path it took. Companion to the P2P from Scratch series.

Readme

p2p-path — heartIT lab #9

Companion lab for Part 1: NAT Hole Punching Explained, of the P2P from Scratch series on heartit.tech.

Connection anatomy: make the invisible visible. The DHT knows things about you that your own machine does not — this lab prints what it knows, stamps every step of a connection with real milliseconds, and reads the resulting path honestly: punched, direct, or relayed.

npx @heart-it/p2p-path swordfish

Same phrase, two terminals — better, two networks (laptop + phone hotspot), because same-LAN peers connect directly and skip the punch entirely.

What you'll see

  • the topic (a salted hash of your passphrase — the DHT sees the hash, never the phrase)
  • [t+Nms] timeline stamps as they truly happen: topic joined, announce landed on the DHT, connection open — lines land in real async order, which can vary between runs
  • a [you] block once the DHT has finished measuring you: your external address as remote nodes sampled it (on a port-randomizing NAT the port reads (varies) — there is no consensus port, and that is the diagnosis), firewalled true/false from an actual inbound probe, your nat port behavior (consistent vs randomized), and your dht role (ephemeral vs persistent)
  • a [path] block per connection: the remote host:port and what range it sits in (loopback / private / CGNAT 100.64/10 / public), our local socket port, an evidence line — the node's own punch and relay counters diffed across this connection — and a verdict built from that evidence, not from the address
  • one tiny message each way, so you see the path carry data; then it stays open until ctrl+c

What it maps to

  • NAT behavioral classes — the [you] block is the node's live NAT test: consistent NATs reuse one external port (the easy punch), randomizing NATs pick a fresh port per destination (the hard case, solved by birthday-paradox port spraying). firewalled decides who must fire first: a firewalled node cannot be cold-dialed — every connection to it starts with its own outbound packet.
  • Holepunching is a timing problem — both sides must fire UDP inside the same coordinated window, signaled through the DHT. That's why every line carries a millisecond stamp: a "dead peer" is often just a slow timeline, not an unreachable machine.
  • Why the address can't prove the path — a hole-punched and a relayed connection both show a public host:port (for a relayed one it's the relay's address). The connection object carries no per-connection relay flag, so the lab diffs the DHT node's honest counters instead — dht.stats.punches (open / consistent / random) and dht.stats.relaying.successes — and when none of them moved, the verdict says unproven rather than guessing.
  • DHT node lifecycle — announcing stores your signed keypair record on the nodes closest to the topic hash; flush() resolves only once that record has actually landed. Every node starts ephemeral (queries, stores nothing, so churn never hurts the routing tables) and graduates to persistent only after ~20 minutes of stable, un-firewalled uptime.

Publishing checklist (maintainer)

npm (current distribution):

npm publish --access public

Pear (when pear install stabilizes): the pear block in package.json is already staged for it. Mind two things:

  1. pear touch mints the pear:// link once; then pear stage pear://<key> . and pear release pear://<key>; keep it available with pear seed pear://<key> on an always-on peer.
  2. pear stage has no default ignores since v2.4 — the pear.stage.ignore list in package.json is load-bearing. Anything staged into the drive is public and content-addressed forever; verify with pear info before the first release. (pear run is deprecated/removed — end users arrive via pear install once it ships stable.)

The lab stays small on purpose. Readers should be able to hold the whole thing in their head — that's the point.