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

stash-sh

v0.1.0

Published

ntfy moves signals. stash moves bytes. A topic-addressed artifact mailbox: curl a file up from one machine, curl it down from another.

Readme

The one-liner

# machine A, tonight
curl -T dist.tgz https://stash.legible.sh/rollout-h7x2rq/dist.tgz

# machine B, tomorrow — or even before A uploads: ?wait= long-polls until it lands
curl -fo dist.tgz "https://stash.legible.sh/rollout-h7x2rq/dist.tgz?wait=60"

The hosted API at stash.legible.sh is in soft launch. To run the identical API yourself: npx stash-sh serve and point the same commands at http://localhost:4188.


Agents keep needing to move a file between two sessions that will never overlap. Session A is Claude Code on your laptop, finishing a build before shutdown. Session B is tomorrow's cloud sandbox that needs the tarball. Every existing answer is wrong in the same way: upload services mint a random URL after the upload, which session A must then relay to session B out-of-band — exactly the plumbing you don't have. With stash, the address is agreed on before the artifact exists, in the prompt: "stash it at rollout-h7x2rq/dist.tgz". Session A uploads to that name. Session B downloads from that name — hours later, from another machine, no coordination channel required. It's ntfy's pattern applied to bytes: the topic is the rendezvous.

The whole API

| Request | What happens | |---|---| | PUT /{topic}/{filename} | Upload bytes (curl -T native; POST --data-binary @f works too). Options: X-TTL: 30m\|12h\|7d\|<seconds> (default 24h, max 7d), X-Burn: N (delete after N downloads). → 201 {url, topic, name, size, expires, sha256, downloadsLeft?}. Re-PUT of the same name replaces it. | | GET /{topic}/{filename} | The bytes. Content-Type guessed from extension, Content-Disposition set, X-Checksum: sha256:…, X-Expires, X-Downloads-Left. Counts against the burn counter; deletes at zero. ?wait=60 long-polls until the file exists (max 300s). 404 after expiry/burn — JSON for curl, a friendly page for browsers. | | HEAD /{topic}/{filename} | Same headers, no body — and never consumes a burn download. | | GET /{topic} | curl: {topic, files: [{name, size, expires, downloadsLeft, sha256}]}. Browsers: an HTML listing with download links and expiry countdowns. Accept: text/event-stream: live SSE — a list event on connect, then named put/delete events with JSON data, heartbeat comments every 25s. | | DELETE /{topic}/{filename} | Remove early. → {deleted: true, topic, name}. | | GET / | Usage — plain text for curl, HTML for browsers. | | GET /README.md · /llms.txt | These docs, as text, for agents. Never token-gated; GET / with Accept: text/markdown serves the README too. Root-level only — /{topic}/README.md stays a stored file. |

Topics match [a-zA-Z0-9_-]{1,64} and are created by first use. Filenames start alphanumeric, then [a-zA-Z0-9._-], max 128. Errors are {"error": "...", "code": "...", "hint": "..."} with honest status codes — the hint spells out the correct next request. Limits — 25 MB/file, TTL ≤ 7d, 100 files/topic — are exported constants in src/limits.mjs.

Teach your agent

Paste this into your CLAUDE.md / AGENTS.md:

## stash — move files between machines/sessions (https://stash.legible.sh)
Pick one unguessable topic per job (e.g. handoff-x7k2mq9f) and treat it like a password.
  upload      curl -T dist.tgz https://stash.legible.sh/{topic}/dist.tgz
  download    curl -fo dist.tgz https://stash.legible.sh/{topic}/dist.tgz
  wait for it curl -fo dist.tgz "https://stash.legible.sh/{topic}/dist.tgz?wait=60"   # long-poll until uploaded
  list        curl https://stash.legible.sh/{topic}
  peek        curl -I https://stash.legible.sh/{topic}/dist.tgz    # HEAD: metadata, never burns
  delete      curl -X DELETE https://stash.legible.sh/{topic}/dist.tgz
Upload options: -H "X-TTL: 7d" (default 24h) and -H "X-Burn: 1" (delete after 1 download).
Every upload response and X-Checksum download header carries the sha256 — verify it.
Files expire (24h default, 7d max), 25 MB max. Re-upload to the same name replaces.

Self-hosting

npx stash-sh serve --data-dir ~/.stash        # http://127.0.0.1:4188

Or clone and run:

git clone https://github.com/legible-sh/legible.git
cd legible/stash
npm start -- --data-dir ~/.stash

| Flag | Default | Does | |---|---|---| | --port | 4188 | Listen port | | --host | 127.0.0.1 | Bind address (0.0.0.0 to expose) | | --data-dir | temp dir + a loud warning | Blobs + a JSONL log, replayed on boot. Set this for any real use. | | --token | off | Require Authorization: Bearer <t> on every topic route. The bytes are the sensitive data, so reads are gated too; only GET /, /README.md, and /llms.txt stay open. | | --base-url | derived from Host header | Absolute URLs in responses and pages (set it behind a proxy) | | --max-size | 25mb | Per-file cap, e.g. --max-size 200mb |

State is legible: blobs live under <data-dir>/blobs/, metadata in <data-dir>/meta.jsonl. Boot replays the log, drops expired files, compacts, and sweeps orphaned blobs.

CLI

The CLI is sugar over the same HTTP API — curl remains the contract. Base URL: --url, else $STASH_URL, else https://stash.legible.sh. Token: --token, else $STASH_TOKEN.

stash put <topic> <file...> [--ttl 24h] [--burn 1]   # prints url + sha256
stash get <topic> <name> [-o out] [--wait 60]        # verifies sha256; -o - for stdout
stash ls <topic>
stash rm <topic> <name>
stash serve [--port 4188] [--host H] [--data-dir D] [--token T] [--base-url U] [--max-size 25mb]

Pro

Planned for the hosted instance, never gating the core verbs — self-host stays complete:

  • Bigger files (the 25 MB cap is a hosted-abuse control, not a technical one — self-hosters already have --max-size).
  • Longer TTLs — retention past 7 days.
  • Read/write tokens — split upload and download rights on a topic without self-hosting.
  • Virus scanning and content policies on the hosted instance.
  • Orgs — shared token management and usage visibility for teams.

Straight talk

  • The topic name is the whole access model. Anyone who can guess it can read, overwrite, or burn your files. Use high-entropy names (handoff-x7k2mq9f, not test); use --token when self-hosting anything sensitive. This is capability-by-obscurity, same trade as ntfy — we're honest about it, you should be too.
  • A download counts when it starts, not when it finishes. An interrupted transfer of a X-Burn: 1 file spends the download. Use HEAD to peek and X-Burn: 2 if you're nervous — that's why the counter takes an N.
  • Two racing downloads of a burn-1 file can both win. The window is one in-process await; it exists. If exactly-once matters, put a mutex in front (the family has one).
  • Files are buffered in memory per request — right for ≤ 25 MB artifacts, wrong for a CDN. Don't put a CDN behind this.
  • A hosted file mailbox is an abuse magnet. Exfil and malware distribution are the existential risks for stash.legible.sh; it ships with rate limits, scanning, and reporting or it doesn't ship. Self-hosting has none of those concerns — it's your disk. That's why self-host is the first-class path today.
  • Everything expires. 7 days is the ceiling by design. stash is a mailbox, not storage — if you want an archive, you want S3.

The family

stash is one of the legible primitives, each an instantiation of the same principles — the whole API in a prompt, curl as the SDK, zero ceremony:

| | | |---|---| | gate (4180) | ntfy tells you things. gate asks you things. | | bigred (4181) | The big red button for your agent fleet. | | trail (4182) | The flight recorder for agent runs. | | slate (4183) | The blackboard from the multi-agent papers, as a URL. | | relay (4184) | The work queue your agents can provision themselves. | | mutex (4185) | flock(1) for agents that live on different machines. | | quorum (4186) | Coordination for agents that do not share a parent process. | | meter (4187) | The kill-brake for agent spend. 429-as-a-service. | | stash (4188) | ntfy moves signals. stash moves bytes. | | tally (4189) | StatHat reborn as ntfy. Three months too late — or right on time. |

License

MIT.