A reusable module for end-to-end encrypted communication in your own apps — using the same kind of protocol as Signal.
Shade is a monorepo that implements secure messaging encryption between two parties (for example browser and server, or two clients). Messages are encrypted so the transport layer (HTTP, WebSocket, etc.) only sees opaque bytes — not the content.
ShadeSessionManager (from @shade/core) together with a CryptoProvider (e.g. Web Crypto in the browser/Bun) and storage. Then call encrypt / decrypt per peer, just like in the demo code demo.ts.
The first message to someone new performs key agreement (X3DH). After that each message uses the Double Ratchet: fresh message keys and periodic DH steps provide forward secrecy (past messages do not survive key compromise) and post-compromise security (the system “recovers” over time after a compromise).
The protocol. X3DH, Double Ratchet, session shapes, errors. No platform crypto here — only the CryptoProvider interface.
ShadeSessionManager — high-level API: initialize, createPreKeyBundle, initSessionFromBundle, encrypt, decryptCrypto implementation for web/Bun/Node via SubtleCrypto — X25519, Ed25519, HKDF, HMAC, random bytes.
shade-core in the browser and on servers that support Web CryptoBinary wire format for messages: version + type + length-prefixed fields (big-endian).
0x01 = PreKeyMessage, 0x02 = RatchetMessageShadeEnvelope efficiently on the wireTransport adapters — not encryption itself, but how you move bytes (e.g. fetch or WebSocket).
Prekey server (Hono) — stores public keys so Alice can start a conversation while Bob is “offline”.
POST /v1/keys/register — register identity + bundleGET /v1/keys/bundle/:address — fetch bundle (consumes one-time prekey when available)POST /v1/keys/replenish — replenish one-time prekeysprocessPreKeyMessage clears it from storage). Improves protection against certain attacks when many clients connect to the same recipient.
Click “Next step” to walk through the sequence as Shade builds it. This mirrors ShadeSessionManager and the demo in the repo.
X3DH solves “I want to talk to Bob now, but Bob may not reply until later”. Bob publishes a prekey bundle on the server. Alice fetches it, runs 3 or 4 DH operations (depending on whether a one-time key is used), and derives a shared root key both parties can reconstruct — without the server learning the secret.
Double Ratchet uses that root as a starting point. For each message (or when new DH keys spin), keys are derived; on the wire payloads are AES-GCM with authentication (AAD binds ciphertext to the ratchet header). The protocol also tolerates messages arriving out of order within limits (MAX_SKIP).
Signal specifications (English): X3DH · Double Ratchet.
Treat Shade as three layers you combine as needed:
Reference path: bun demo.ts from the repo root shows a frontend/backend flow with memory storage and real crypto primitives.