@meshwhisper/sdk
v0.5.0
Published
Serverless P2P E2EE Messaging SDK
Downloads
557
Readme
The problem with messaging
Building messaging yourself takes months and you'll get the cryptography wrong. Hosted services like Sendbird cost $2,000/month and can read every message your users send.
MeshWhisper is different. You run the backend. It costs €4/month. And it cannot read your users' messages — not as a policy, but by design.
How it works
sequenceDiagram
autonumber
participant A as Alice's device
participant R as Your Node (relay)
participant B as Bob's device
Note over A,B: All encryption happens on-device.<br/>The relay only sees ciphertext.
A->>A: Encrypt with Double Ratchet<br/>(PQXDH session key)
A->>R: Send {destHash, ciphertext}
Note right of R: Stores opaque blob.<br/>Cannot decrypt — ever.
R-->>B: Wake push (no content)
B->>R: Pull queued blobs
R->>B: Deliver ciphertext
B->>B: Decrypt locally- Messages are encrypted on-device with the Signal protocol (X3DH + Double Ratchet) before they leave the user's device
- Your Node sees only encrypted ciphertext. It routes, stores, and delivers — nothing more
- Push notifications wake the app when a message arrives while it is closed — without the push service seeing message content
- Sessions, identity, and message history persist automatically across page reloads and restarts
- Encrypted backup is included — contacts and message history are backed up to your own relay as opaque AES-GCM blobs. No Google Drive, no iCloud, no separate recovery code. Sign in with the same username and password on any device to restore.
What you deploy
flowchart LR
subgraph Server["Your VPS · ~€4/month"]
direction TB
N["meshwhisper-node<br/>relay · store-and-forward<br/>media · key directory"]
P["meshwhisper-push<br/>APNs · FCM · Web Push"]
DB[("SQLite")]
N --- DB
N <--> P
end
subgraph Devices["Your users' devices"]
direction TB
A["@meshwhisper/sdk<br/>PWA · React Native · Node.js"]
end
A <-->|"wss · ciphertext only"| N
P -. "content-free wake" .-> ATwo Docker containers alongside whatever is already on your server:
services:
meshwhisper-node:
build: ./node
environment:
BASE_URL: "https://relay.myapp.com"
PUSH_WEBHOOK_URL: "http://meshwhisper-push:4000/notify"
meshwhisper-push:
build: ./push-service
environment:
VAPID_PUBLIC_KEY: "${VAPID_PUBLIC_KEY}"
VAPID_PRIVATE_KEY: "${VAPID_PRIVATE_KEY}"
VAPID_SUBJECT: "mailto:[email protected]"docker compose up -dThat is your entire messaging backend. Relay, store-and-forward, push notifications, media hosting, and key exchange directory — all in one container stack.
What you add to your app
npm install @meshwhisper/sdkimport { MeshWhisper } from '@meshwhisper/sdk';
const mw = await MeshWhisper.init({
namespace: 'com.example.myapp',
node: 'wss://relay.myapp.com',
onMessage: (message) => {
const text = new TextDecoder().decode(new Uint8Array(message.payload));
appendToChat(message.senderId, text);
},
});
// Share this with contacts
const myId = mw.getLocalPeerId();
// Send — X3DH key exchange happens automatically on first contact
await MeshWhisper.send(contactId, new TextEncoder().encode('Hello!'));The SDK auto-detects the environment. In a browser it uses IndexedDB for storage and the native WebSocket API. In Node.js it uses the filesystem and the ws package. No configuration required.
Learn by reading a real app
Prudence is a complete PWA built on MeshWhisper — direct messages, groups, media, push, multi-device archive sync, conversation history recovery — and is maintained as a living reference for SDK integrators. Every major SDK feature is exercised in the codebase. The annotated map in prudence/REFERENCE.md points each SDK feature to the file and function where it's wired, and calls out the non-obvious patterns (control-message prefixes, archive-sync semantics, identity persistence) that an integrator would only learn by reading the code.
Try it live at prudence.meshwhisper.org or run it locally from prudence/.
For server-side integrations — AI agents, customer-service bots, IoT gateways — see examples/. The first reference is examples/support-bot: a customer-service agent running as a MeshWhisper peer, ~150 lines, with an echo mode (no API key) and an LLM mode (Claude). Same protocol, different deployment shape.
Scaffold in 60 seconds
npx @meshwhisper/cli initAsks for your namespace, where your app should connect (Foundation relay for development, your own server for production), and what kind of app you're building. Writes a working SDK skeleton — and for self-hosted deployments, a complete meshwhisper-node/ directory: Docker Compose, standalone Dockerfiles, generated Web Push keys, and a federation bootstrap so your relay joins the mesh. npx @meshwhisper/cli doctor health-checks any node.
What it costs
| | MeshWhisper | Sendbird | Build it yourself | |---|---|---|---| | Monthly cost | ~€4 (your VPS) | $2,000+ | Your engineers' time | | Can read messages | No — impossible | Yes | Depends on you | | Time to integrate | 2–4 hours | 1–2 days | Minutes | | Push notifications | Included | Add-on | Build separately | | Message history | Included | Included | Build separately | | Delivery receipts | Included | Included | Build separately | | Media sharing | Included | Add-on | Build separately |
What it is good for
MeshWhisper is the right choice when privacy is a feature, not an afterthought:
- Healthcare — patient-doctor messaging where you genuinely cannot access patient communications
- Legal — lawyer-client communications where privilege requires confidentiality
- Finance — advisor-client messaging with regulatory pressure toward E2EE
- B2B SaaS — telling enterprise customers "we cannot read your data even if subpoenaed"
- Any app where your users' privacy matters and you want to prove it structurally, not just claim it in a privacy policy
Your node, your users, your namespace
Every MeshWhisper deployment is self-contained. Your node serves your app's namespace. Your users connect to your node. Messages between your users flow through your node. Nothing depends on shared infrastructure, third-party availability, or anyone else's decisions.
Node operators can also choose to peer: relay-to-relay federation is live, and FEDERATION_MODE=open joins the mesh with one bootstrap entry — your node forwards packets for other relays and they for yours. But federation is optional. Your app works in complete isolation from day one.
Packages
| Package | Purpose |
|---|---|
| @meshwhisper/sdk | Client SDK — browser, Node.js, React Native |
| @meshwhisper/cli | npx @meshwhisper/cli init scaffolding |
| @meshwhisper/node | Relay server binary |
| @meshwhisper/push-service | APNs / FCM / Web Push dispatcher |
| @meshwhisper/service-worker | PWA push event handler |
Documentation
- docs/getting-started.md — complete walkthrough from zero to working PWA
- docs/api.md — full SDK API reference
- prudence/REFERENCE.md — Prudence as a living example: which SDK feature lives where in the demo app, and the non-obvious patterns to copy
- examples/ — Minimal end-to-end integrations. Start with support-bot — a customer-service AI agent as a MeshWhisper peer, ~150 lines. Or local-first — on-site human and machine-to-machine comms that keep working when the relay (or the internet) is gone.
- docs/identity-patterns.md — Six identity-derivation patterns (username+password, email, phone, OS keychain, Secure Enclave, passkey) — pick what matches your threat model; the SDK is neutral
- docs/self-hosting.md — server configuration, all environment variables
- docs/local-networks.md — running on isolated/air-gapped networks; LAN peer-to-peer delivery so conversations survive losing the relay
- docs/federation.md — relay-to-relay peering protocol (open mode joins the mesh with one env var and one bootstrap entry)
- docs/p2p-transport.md — opportunistic direct-transport spec; what's shipped (LAN) and what's demand-gated (WebRTC, proximity radio)
- docs/codebase-overview.md — technical overview for contributors and reviewers
- docs/direction.md — what MeshWhisper is today, where it's going, what we're deliberately not building
- docs/adr/ — architecture decision records: the why behind significant choices
- docs/multi-device.md — strategy for multi-device support (hand-off, hard boot, linked devices)
- docs/whitepaper.md — full protocol whitepaper (PDF)
- docs/presentation.pdf — slide deck overview of the protocol and project
Security model
- Key exchange uses PQXDH — a hybrid of X3DH (X25519) and ML-KEM-768. Sessions are post-quantum secure from the first message.
- Session encryption uses the Double Ratchet algorithm. Each message uses a fresh key; compromise of one key does not expose past or future messages.
- Your Node sees only encrypted ciphertext and anonymous destination hashes. It cannot identify senders or read content.
- Destination hashes rotate every hour, limiting traffic-analysis windows.
- The push service receives a device token and a destination hash — no message content.
- Identity keys are generated on-device and never transmitted. Private keys are stored only in the device's configured storage backend.
- Media is encrypted locally before upload. The Node stores ciphertext. The decryption key travels through the ratchet-encrypted message channel — never through the Node's HTTP API.
- Safety numbers — both parties can verify a 60-digit code out-of-band to confirm no MITM. Computed from a sorted BLAKE3 hash of both Ed25519 identity keys.
For a detailed technical overview see docs/codebase-overview.md.
