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

@dotprotocol/piperchat

v1.5.0

Published

P2P chat with E2E sealed bodies (X25519+AES-256-GCM), Ed25519-signed identities, optional zstd-with-shared-dict wire compression, signed-when-present threading + reactions + read-acks, SQLite storage, SSE relay

Readme

piperchat

End-to-end encrypted P2P chat in 500 KB. No signup. No server-side decrypt. Embed in any web app in 30 seconds.

License: Apache 2.0 v1.5.0 node ≥22 CI dot-protocol

Quick start · Embed in your app · Self-host · Protocol spec · CHANGELOG


   ┌─────────────┐                       ┌─────────────┐
   │  Alice      │  ─── X25519 + AES ──▶ │  Bob        │
   │  (browser)  │                       │  (browser)  │
   └──────┬──────┘                       └──────▲──────┘
          │                                     │
          ▼                                     │
   ┌──────────────────────────────────────────────────┐
   │   piperchat relay  (stores ciphertext only)       │
   │   • E2E sealed bodies — server cannot decrypt     │
   │   • Ed25519-signed envelopes — tamper-evident     │
   │   • zstd-with-shared-dict — 34.8% smaller wire    │
   │   • iroh P2P fallback — direct when NAT permits   │
   └──────────────────────────────────────────────────┘

A self-hostable, OSS-first alternative to Slack/Discord/Matrix for teams who want real E2E encryption and embeddable group chat without the operational weight of Matrix Synapse or the Mongo footprint of Rocket.Chat.

The server holds ciphertext. Only recipient keys decrypt. Drop it into a customer-facing product, a CRM, a project tool — anywhere you'd otherwise reach for Intercom or a 3rd-party chat SaaS.


Why piperchat

| | piperchat | Matrix Synapse | Rocket.Chat | Discord/Slack | |---|:---:|:---:|:---:|:---:| | End-to-end encryption | ✅ default | optional (Olm) | optional | ❌ | | Server can read messages | ❌ never | ❌ when on | ❌ when on | ✅ always | | Self-host complexity | one binary | postgres + nginx + workers | mongo + nginx + workers | n/a | | Embed in your own app | ✅ iframe + postMessage | ❌ | partial | ❌ | | RAM at idle | ~50 MB | ~600 MB | ~1.5 GB | n/a | | Wire format | open (zstd + AES-GCM) | open (Megolm) | proprietary | proprietary | | Lines of server code | ~1,200 LOC | ~60,000 LOC | ~250,000 LOC | closed | | License | Apache 2.0 | Apache 2.0 | MIT | proprietary | | Account required | ❌ | ✅ | ✅ | ✅ |


Quick start

git clone https://github.com/dot-protocol/piperchat
cd piperchat && npm install && npm start

Open http://localhost:4100 in two browser tabs. Pick a username, send a message. Done — that's end-to-end encrypted chat with no signup and no database setup.

Port defaults to 4100. Override with PORT=8080 npm start.


Embed in your app

The most common reason teams reach for piperchat: drop encrypted chat into your own product without rebuilding the wheel.

<iframe
  src="https://chat.yourcompany.com/?embed=1&theme=light&accent=%232563eb"
  style="border:0; width:100%; height:600px;"
></iframe>

<script>
const iframe = document.querySelector('iframe');

// Wait for the chat to boot, then inject the logged-in user's identity
window.addEventListener('message', (e) => {
  if (e.data.type === 'piperchat:ready') {
    iframe.contentWindow.postMessage({
      type: 'piperchat:set-identity',
      ed25519_priv: USER_KEYS.signing,    // from your auth layer
      x25519_priv:  USER_KEYS.encryption, // never sent to your server
      username:     currentUser.handle,
      channel:      'support-' + currentUser.companyId,
    }, '*');
  }
  // Receive metadata-only notifications (body never leaves the iframe)
  if (e.data.type === 'piperchat:message-received') {
    showNotificationBadge(e.data.payload);
  }
});
</script>

What you get for free:

  • theming via CSS variables (?accent=…&bg=… URL params or postMessage('piperchat:set-theme') at runtime)
  • identity injection from your existing auth layer — keys live in the iframe
  • metadata-only host bridgefrom, channel, ts flow out; ciphertext never does
  • per-tenant channels with admin RBAC (claim → add member → enforce)

Working examples in examples/:

Full postMessage API in examples/README.md.


Self-host

Docker

docker compose up

Persistent volume piperchat-data carries the SQLite store + dictionary.

nginx + TLS

location / {
  proxy_pass         http://127.0.0.1:4100;
  proxy_buffering    off;            # SSE needs this
  proxy_cache        off;
  proxy_read_timeout 24h;
  proxy_http_version 1.1;
}
certbot --nginx -d chat.yourdomain.com

pm2

npm install --omit=dev
pm2 start deploy/ecosystem.config.js
pm2 save && pm2 startup

Logs in ./logs/. Memory + restart config in deploy/ecosystem.config.js.

Environment

| Variable | Default | What it does | |---|---|---| | PORT | 4100 | HTTP port | | DATA_DIR | ./data | SQLite + uploads directory | | DB_PATH | ${DATA_DIR}/piperchat.db | Override DB location | | VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY | — | Enable browser push (v1.5) | | POSTHOG_API_KEY | — | Opt-in event telemetry (no-op when unset) |


Group admin (v1.5)

Channels are open by default. Once claimed by an owner, only members may post. Owner + admins manage the roster.

# 1. owner claims a channel (first-write-wins)
curl -X POST https://chat.example.com/channels/design/claim \
  -d '{"ts":...,"ed25519_pub":"...","sig":"...","description":"UI/UX team"}'

# 2. add a member
curl -X POST https://chat.example.com/channels/design/members \
  -d '{"ts":...,"ed25519_pub":"...","sig":"...","target_dot1":"dot1:...","role":"member"}'

# 3. see the full roster + audit log
curl https://chat.example.com/channels/design/admin

Or just open /admin in the browser — it's a full UI for claim, roster, roles, and the audit log. All admin actions are ed25519-signed with a ±300s replay window.


Use it from code

const { PiperClient } = require('piperchat/client');

const c = new PiperClient({
  url: 'http://localhost:4100',
  author: 'agent-richard',
  // ed25519 + x25519 keys (see docs/PROTOCOL.md)
});

await c.sendEncrypted('hello bob', 'direct');

c.subscribe((msg) => {
  console.log(`[${msg.from_username}] ${msg.content}`);
});

See examples/agents/ for a working two-agent demo, examples/ for embed + CRM examples, and docs/PROTOCOL.md for the full wire format spec.


Performance

Measured on the bundled test suite (Apple Silicon M-series, Node 22):

| Metric | Value | |---|---:| | Server RAM at idle | ~50 MB | | Server RAM at 1k msg/s | ~120 MB | | Wire reduction (meaningful messages ≥100B) | −34.8% vs uncompressed | | Compression ratio (mixed corpus) | 1.62× median | | Round-trip compress + decompress (p99) | 0.68 ms | | Sealed-body encrypt (p99) | 0.8 ms | | End-to-end latency (relay path, local) | < 5 ms |

Full release notes →


Architecture

Each running instance is a node. Nodes exchange messages two ways:

  1. P2P (iroh) — Nodes share an iroh document; GET /ticket returns an address blob, POST /connect joins. Direct connection across NAT, no central server.
  2. Relay (SSE) — When P2P can't establish (firewalled, CI), the same HTTP server fans messages out to all connected browsers via Server-Sent Events.

Both paths see only the sealed envelope. The body is encrypted with AES-256-GCM, the key is wrapped per-recipient with X25519 ECDH + HKDF-SHA256, the whole envelope is signed Ed25519. The server stores ciphertext and forwards it. It has no decryption capability.

post /messages    ─▶ verify sig → store ciphertext → fan out via SSE
get  /messages    ◀─ return ciphertext (anyone), recipient decrypts
get  /events      ◀─ SSE stream of ciphertext envelopes
post /channels/X/claim       — ed25519-signed channel ownership
post /channels/X/members     — owner/admin grants membership
get  /admin                  — admin panel UI

Full route map in docs/PROTOCOL.md.


Security model

| Property | Guarantee | |---|---| | Server can read message bodies | ❌ never (ciphertext only) | | Server can forge messages | ❌ no (no signing key) | | Server can drop messages | ✅ yes (relay can censor; use P2P to bypass) | | Server can correlate metadata | ✅ yes (from, to, ts, channel are visible) | | Forward secrecy | ⚠️ not yet — long-lived X25519 keys (v2.0 plans Double Ratchet) | | Quantum resistance | ❌ no — X25519 is classical only | | Compression oracle attacks | mitigated by per-message random nonces and shared-dict static training |

Found something? Open an issue or email [email protected].


Roadmap

  • [x] v1.0 — Plaintext chat over SSE/iroh
  • [x] v1.1 — Ed25519 signed envelopes + DOT-native identity
  • [x] v1.2 — X25519 + AES-256-GCM sealed bodies (E2E encryption)
  • [x] v1.3 — Username registry
  • [x] v1.4 — zstd-with-shared-dictionary wire compression
  • [x] v1.5 — Onboarding wizard, browser push, embed mode, admin panel + RBAC
  • [ ] v1.6 — Inline image previews, reactions, reply threading UI
  • [ ] v1.7 — Mobile-native composer, drag-drop attach
  • [ ] v2.0 — Double Ratchet forward secrecy + post-compromise security
  • [ ] v2.x — Federation between piperchat instances

Track active work in GitHub issues.


Contributing

We're a small team building this in public. The fastest way to help:

  1. Star this repo — it's the single best signal that this matters
  2. Run it locally and open an issue when something feels off
  3. Pick a v1.6 checkbox above and open a PR

Code style: no framework, no bundler, no transpile step. Vanilla JS, Node built-ins, one dependency for sqlite (better-sqlite3) and one for signing (tweetnacl). The whole server is ~1,200 LOC. Read it in an afternoon.

npm test

runs the full v1.2/v1.4/v1.5 round-trip suite plus compression invariants.


Credits

Standing on the shoulders of:

  • iroh (n0-computer) — P2P doc sync — MIT/Apache-2.0
  • tweetnacl-js (dchest) — Ed25519 + X25519 — CC0
  • better-sqlite3 (WiseLibs) — sync sqlite — MIT
  • zstd-codec — zstandard in JS — MIT
  • Node.js built-inshttp, fs, crypto. No framework. No bundler.

License

Apache 2.0. Build anything. Ship it. Tell us what you made.

⭐ Star on GitHub · 💬 Live demo · 📖 Protocol spec · 🐛 Report a bug

Built by humans + agents at Pied Piper. Carrying the encrypted-everything torch since 2026.