Skip to content

vorobalek/whisper-client

Repository files navigation

Whisper Messenger

Statements Lines Functions Branches

Open in GitHub Codespaces

whisper-app is a demonstration of the Whisper protocol in action. Try it live: https://whisper.vorobalek.dev

whisper-server is the server-side reference implementation of the Whisper protocol.

This app showcases the capabilities of the Whisper protocol. Below you'll find a detailed protocol description and usage examples for integration into your own projects.


πŸ”— PWA Support

The live demo site (https://whisper.vorobalek.dev) is a fully featured Progressive Web App (PWA). You can install it on Android and iOS devices for an app-like experience:

Android

  1. Open the demo site in Chrome (or another supported browser) on your Android device.
  2. Tap the β€œInstall” icon in the address bar or open the menu and select β€œAdd to Home screen.”
  3. Confirm to install the app.

iOS

  1. Open the demo site in Safari on your iOS device.
  2. Tap the β€œShare” button at the bottom of the screen.
  3. Scroll down and select β€œAdd to Home Screen.”
  4. Tap β€œAdd” to confirm and launch the app from your home screen.

✨ Features

  • Multiple dialogs (multi-peer support)
  • Replies
  • Emoji reactions
  • Typing indication
  • Message status indicators (sent / delivered / read)
  • Push notifications (with user consent)
  • Encrypted local storage (password-protected, never leaves your device)
  • End-to-end encrypted WebRTC data channels
  • QR code sharing and scanning for public keys
  • Version update notifications
  • Full transparency of protocol and cryptography (see privacy info popup)
  • Full test coverage and source code are available
  • No registration or disclosure of any personal data required
  • Dual peer-to-peer channels (incoming/outgoing) for enhanced reliability and to circumvent regional restrictions
  • Seamless fallback to relay servers when direct P2P connection is not possible
  • Zero trust required β€” neither backend servers, relay servers, nor counterparties can compromise message confidentiality; security is enforced at the protocol level
  • Private messages are unlinkable β€” it is impossible to determine the sender or recipient, and transmitted messages contain no identifying marks

πŸ—ΊοΈ Roadmap

Planned features (all to be implemented without compromising the core privacy and security principles):

  • Group chats (protocol-level, based on MLS)
  • Voice messages
  • Audio/video calls

All future features will also preserve:

  • Minimal trust in the server or other participants
  • Minimal information stored on the server or transmitted in unencrypted form

πŸ›‘οΈ Overview

Whisper is a privacy-first, end-to-end encrypted messenger protocol and reference application. All cryptographic operations are performed client-side, ensuring that your private data and keys never leave your device unencrypted. The protocol is designed for maximum privacy, security, and user control.

Key Privacy Guarantees:

  • All data transfers are time-sensitive, signed, and verified by both server and recipient.
  • Private messages use end-to-end encryption with a unique key refreshed on each reconnection.
  • Chat history and your private signature key are stored encrypted, protected by your password, and never leave your device.
  • All WebRTC data is transmitted with end-to-end encryption.
  • The Whisper server only stores your public signature key and, if you consent, your push subscription. It relays data without retention or modification.
  • All communication with the server is over HTTPS.
  • No registration, no personal data collection, and no trust required β€” privacy and unlinkability are enforced by design.

See the Whisper Protocol Diagram (SVG) for a technical overview.


πŸ”¬ Protocol Description

The protocol, implemented in whisper-core, provides:

  • Key Generation: Each user generates a signing key pair (for authentication and integrity) β€” this is your persistent identity and "account." Encryption key pairs, in contrast, are generated for every new connection or reconnection to a peer, ensuring that end-to-end encryption keys are always fresh and never reused.
  • Registration: Users register their public signing key and (optionally) push subscription with the server. All registration data is signed and time-sensitive.
  • Connection Establishment: Peer-to-peer connections are established using WebRTC, with signaling and authentication handled via the server. All signaling messages are also signed and time-sensitive.
  • End-to-End Encryption: Each session uses a unique, ephemeral encryption key derived via Diffie-Hellman, never stored or transmitted.
  • Message Exchange: All messages are encrypted and signed. The server only relays encrypted payloads and never has the ability to modify, or inspect this content.
  • Push Notifications: Optional push notifications are supported via VAPID and Web Push, with subscriptions stored only if the user consents.

πŸš€ Getting Started & Usage Example

Running via Dev Container

Open in GitHub Codespaces

  1. Open the project in a development container (see .devcontainer/devcontainer.json).
  2. In the container terminal, install dependencies and start the app:
    npm ci
    npm run start
  3. The app will be available at http://localhost:8080.
  4. Ensure that ports 8080 and 5027 are free on your host machine before starting.
<!-- docs/example.min.html -->
<!doctype html>
<html lang="en">
    <head></head>
    <body></body>
    <script>
        (function (w, h, s, p, r, m, i, n) {
            if (w[p]) return;
            m = 1 * new Date();
            w[p] = {};
            w[p].t = m;
            r = h.getElementsByTagName(s)[0];
            i = h.createElement(s);
            i.async = true;
            i.src = 'https://whisper.vorobalek.dev/core.min.js?_=' + m;
            i.onload = function () {
                n = Whisper.getPrototype(console);
                n.initialize({
                    serverUrl: 'https://cluster.vorobalek.dev/whisper',
                    signingKeyPair: n.generateSigningKeyPair(),
                }).then((x) => {
                    w[p] = x;
                });
            };
            r.parentNode.insertBefore(i, r);
        })(window, document, 'script', 'whisper');
    </script>
</html>
<!-- docs/example.html -->
<!doctype html>
<html lang="en">
    <head></head>
    <body></body>
    <script>
        (function (w, h, s, p, r, m, i, n) {
            if (w[p]) return;
            m = 1 * new Date();
            w[p] = {};
            w[p].t = m;
            r = h.getElementsByTagName(s)[0];
            i = h.createElement(s);
            i.async = true;
            i.src = 'https://whisper.vorobalek.dev/core.min.js?_=' + m;
            i.onload = function () {
                n = Whisper.getPrototype(console);
                n.initialize({
                    onMayWorkUnstably: (reason) => {
                        console.warn(reason);
                    },
                    onTrace: (...args) => {
                        console.trace(...args);
                    },
                    onDebug: (...args) => {
                        console.debug(...args);
                    },
                    onLog: (...args) => {
                        console.log(...args);
                    },
                    onWarn: (...args) => {
                        console.warn(...args);
                    },
                    onError: (...args) => {
                        console.error(...args);
                    },
                    serverUrl: 'https://cluster.vorobalek.dev/whisper',
                    version: `${m}`,
                    onNewVersion: () => {
                        console.warn('Update needed!');
                    },
                    vapidKey: 'BHAYDRAjMWXfg7dxFIOZYNLlxrVDohy_PbN7SXcrXapiZq0Jnt0VXsAx6ytkLArVVFDSfula4VRWm5HDvkVVRbA',
                    onPermissionDefault: () => {
                        console.warn('Permissions default!');
                    },
                    onPermissionGranted: () => {
                        console.warn('Permissions granted!');
                    },
                    onPermissionDenied: () => {
                        console.warn('Permissions denied!');
                    },
                    signingKeyPair: n.generateSigningKeyPair(),
                    onIncomingConnection: (connection) => console.warn('Incoming connection', connection),
                    iceServers: [
                        {
                            urls: 'stun:***:3478',
                        },
                        {
                            urls: 'turn:***:3478',
                            username: '***',
                            credential: '***',
                        },
                    ],
                    focusOnDial: (peerPublicKey) => {
                        console.warn('Dial!', peerPublicKey);
                        return Promise.resolve(true);
                    },
                    requestDial: (peerPublicKey) => {
                        if (window.confirm(`Incoming connection request from ${peerPublicKey}.\nAccept?`)) {
                            return Promise.resolve(true);
                        }
                        console.warn('Declined.', peerPublicKey);
                        return Promise.resolve(false);
                    },
                }).then((x) => {
                    w[p] = x;
                });
            };
            r.parentNode.insertBefore(i, r);
        })(window, document, 'script', 'whisper');
    </script>
</html>

Using the Library from the Browser Console

After integration, you can interact with the protocol directly from the browser console:

// 1. Get your public key
whisper.publicKey;

// 2. Set up a message handler for a peer (replace <peer public key> with the actual key)
whisper.get('<peer public key>').onMessage = (e) => {
    console.warn(e);
};

// 3. Open a connection to the peer
whisper.get('<peer public key>').open();

// 4. Send a message to the peer
whisper.get('<peer public key>').send('Hello, Whisper!');

πŸ§ͺ Test Coverage & Source Code

About

privacy-first, end-to-end encrypted messenger

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •