Skip to content
OpenE2EE

Add end-to-end encrypted messaging to your TypeScript app.

The SDK encrypts on each device. OpenE2EE Relay operates the delivery: encrypted device mailboxes, group fan-out, private attachments, and push wakes.

Fully Open Source. Pure TypeScript. Batteries included.

Built for

    • Expo
    • React
    • Browser
    • Node.js
    • Convex
    • R2
    • S3

npm install @open-e2ee/signal-protocol-sdk

quickstart.ts
import { createSignalProtocolClient } from "@open-e2ee/signal-protocol-sdk";
import { inMemoryStore } from "@open-e2ee/signal-protocol-sdk/local/store/memory";
import { inMemoryRelay } from "@open-e2ee/signal-protocol-sdk/remote/relay/memory";

const relay = inMemoryRelay(); // Devices post and collect envelopes from the relay.

const alice = await createSignalProtocolClient({ // Alice's device.
  identity: { userId: "alice" },
  adapters: { storage: inMemoryStore(), relay }, // Your keys stay in your store.
});
const bob = await createSignalProtocolClient({ // Bob's device. In an app, each runs its own.
  identity: { userId: "bob" },
  adapters: { storage: inMemoryStore(), relay },
});

bob.registerHook("onMessageDecrypted", async (message) => { // Fires after the SDK decrypts.
  console.log(message.content); // plaintext, only on Bob's device
});
bob.startRelaySubscription();

// Encrypted on Alice's device before the relay carries it.
await alice.send("bob", "Dinner at 7. I got us the table by the window.");

Managed encrypted delivery

Relay is the delivery half of an end-to-end encrypted application. Every plan carries every Relay protocol feature. Plans differ by capacity, support, service terms, and approved operational controls.

Free production

Production traffic at $0: 100 Relay MAU, 100,000 delivery units, and 1 GB exact live storage, under hard caps with no automatic charge. Every project also opens a Development environment, which needs no card.

Start a Relay project (opens in a new tab)

What Relay operatesRelay plans and limits

Live demo, in your browser

Type a sentence and the installed SDK encrypts itA scripted conversation plays through the installed SDK against the in-memory relay. Keys stay on the devices. The relay never needs message plaintext or device private keys.

Demo Settings
Protocol
Device A's deviceoffline
    private keys0
    not yet agreed0 message keys derived
    classical
    Fresh elliptic-curve agreements fold new secrets into the session as the devices trade messages.
    chain
    Hashes the running secret forward one notch per message. Each notch yields exactly one message key, and the hash does not reverse.
    post-quantum
    Layers ML-KEM agreements into the same session, so recorded ciphertext stays unreadable to a future quantum computer.
    key generationOne-time setup. This device generates its identity and the batches of single-use keys it publishes, before any message exists.
    post-quantum shareThe ML-KEM part of the row above. It is already counted in key generation, not added to it.
    key agreementAgreeing the first shared secrets with the other device, classical and post-quantum together.
    encryptionSealing one message on this device, measured up to the hand-off to the relay.
    decryptionOpening one arrived envelope, from arrival to plaintext. The first opening also performs this device’s share of the key agreement, so both rows show that one span until a later arrival.
    relaynever needs plaintext
    Device B's deviceoffline
      private keys0
      not yet agreed0 message keys derived
      classical
      Fresh elliptic-curve agreements fold new secrets into the session as the devices trade messages.
      chain
      Hashes the running secret forward one notch per message. Each notch yields exactly one message key, and the hash does not reverse.
      post-quantum
      Layers ML-KEM agreements into the same session, so recorded ciphertext stays unreadable to a future quantum computer.
      key generationOne-time setup. This device generates its identity and the batches of single-use keys it publishes, before any message exists.
      post-quantum shareThe ML-KEM part of the row above. It is already counted in key generation, not added to it.
      key agreementAgreeing the first shared secrets with the other device, classical and post-quantum together.
      encryptionSealing one message on this device, measured up to the hand-off to the relay.
      decryptionOpening one arrived envelope, from arrival to plaintext. The first opening also performs this device’s share of the key agreement, so both rows show that one span until a later arrival.

      relay never needs plaintext

      Alice's deviceoffline

      private keys0

      0 message keys derived

      classical
      chain
      post-quantum
      key generation
      encryption
      decryption

      Bob's deviceoffline

      private keys0

      0 message keys derived

      classical
      chain
      post-quantum
      key generation
      encryption
      decryption
      The same conversation on two phones. The relay between them carries ciphertext and addressing.

        Nothing recorded yet.

        Select a row to see the call that caused it.

        device — bob

        Dinner at 7. I got us the table by the window.

        sealplaintext stops here
        relay — the row in your database

        ciphertext — 3,532 base64 characters, 2,648 bytes decoded, excerpt shown

        targetUserId
        bob
        targetDeviceId
        1
        senderUserId
        alice
        senderDeviceId
        1
        messageType
        prekey_bundle
        timestamp
        1786899316795
        recipientRegistrationId
        11213
        serverTimestamp
        1786899316805

        and a relay-assigned envelope id, in whatever format your relay assigns

        recorded by running the quickstart@open-e2ee/signal-protocol-sdk@0.2.0every field shown is still in the envelope at 1.0.0

        What ships in the box

        Five things that are true the first time you install it.

        • Pure TypeScript, wherever your app runs

          Expo, React Native, browsers, and Node from one package. The protocol code is pure TypeScript with no native crypto module to link.

        • Post-quantum

          PQXDH with ML-KEM-1024 establishes every session, and the Triple Ratchet carries it forward: the Double Ratchet plus the ML-KEM Braid. On without configuration, and failing closed.

        • Batteries included

          Multi-device, groups, sealed sender, safety numbers, encrypted attachments and files ship in the same package as the one-to-one case.

        • Published for anyone to review

          The implementation, the protocol profile, and the threat model are public, with every deliberate divergence from the specifications documented.

        • Infrastructure you own

          Storage, relay, and object storage are interfaces you fill. Each has a shipped adapter, or you write your own. Self-host them on your hardware, a VPS, or your cloud; the SDK itself does not phone home. Or use OpenE2EE Relay for managed encrypted delivery. A self-hosted relay holds ciphertext and routing metadata: all your backend can leak and all it can produce for a legal request. Search, moderation, and restoring a user who has lost every device stay yours to design.

        The Expo, Node, browser, and bare React Native stores implement the storage interface in full. What each store holds.

        Open Source

        The complete SDK is free under AGPLv3, and a commercial license covers closed-source products. The build is the same either way. Every feature is in the package, and none of it is time-limited.

        1. read it
        2. run it
        3. share it
        4. build on it

        SDK on GitHub (opens in a new tab)LicensingPricing

        The OSI logo trademark is the trademark of Open Source Initiative.