DEV Community

Cover image for Whisper Board: Building a Private, Decentralized Messaging Experience with Waku
Abdul-Qawi Laniyan
Abdul-Qawi Laniyan

Posted on

Whisper Board: Building a Private, Decentralized Messaging Experience with Waku

Decentralized messaging is the future of censorship-resistant, privacy-first communication.

Whisper Board is a lightweight, anonymous chat app powered by Waku protocol, designed to showcase how easy it is to integrate Waku into a modern web stack.

Here is the repo and live app

Why Decentralized Messaging?

Most recently, we've seen how fragile centralized systems can be. A single AWS outage in the US-East region, knocked out major apps, even Signal, a privacy-first messenger, across Central and Eastern Europe. Billions of messages delayed, communities cut off, and trust shaken.
Here is a full report on what happened.

When all communication routes depend on a few corporate data centers, resilience becomes an illusion.

Decentralized messaging changes that: it doesn’t rely on one server, company, or geography. Messages flow peer-to-peer, so even if one node fails, the network stays alive. It’s more than just privacy, it’s about robustness, autonomy, and continuity.

In this article, you'll learn:

  • The architecture behind a simple Whisper Board built with waku
  • How to connect to and use Waku in your own app
  • How messages flow through the network
  • How to onboard and contribute as a new developer
  • Visual diagrams to aid understanding

Architecture Overview

Diagram: System Components

whisper board flow

File structure :

src
├── components
│   ├── MessageCard.tsx
│   └── MessageForm.tsx
├── hooks
├── lib
├── pages
│   ├── Index.tsx
│   └── NotFound.tsx
├── services
│   ├── MessageService.ts
│   └── WakuService.ts
├── types
├── App.css
├── App.tsx
├── index.css
├── main.tsx
├── vite-env.d.ts
.gitignore
bun.lockb
components.json
eslint.config.js
index.html
package-lock.json
package.json
postcss.config.js
README.md
tailwind.config.ts
tsconfig.app.json
tsconfig.json
tsconfig.node.json
vite.config.ts
</pre>
Enter fullscreen mode Exit fullscreen mode
  • Frontend: Built in React, styled with Tailwind and shadcn-ui.
  • Hooks: useWaku handles node connection, useMessages manages sending/receiving.
  • Services: WakuService manages the Waku node, MessageService handles message flow.
  • Waku Node: Lightweight node connects to the decentralized network.

Technologies Used

- Vite
- TypeScript
- React
- shadcn-ui
- Tailwind CSS
- Waku JS SDK
Enter fullscreen mode Exit fullscreen mode

How the App Works

Connecting to Waku

The custom hook useWaku.ts initializes and manages the Waku node:

const { isConnecting, isConnected, error } = useWaku();
Enter fullscreen mode Exit fullscreen mode
  • On mount, calls wakuService.initialize() to connect to Waku peers.
  • Cleans up on unmount via wakuService.stop().

Code Reference

//src/hooks/useWaku.ts 
/* https://github.com/Oladotunlaniyan/whisper-waku-chat/blob/main/src/hooks/useWaku.ts*/

import { useState, useEffect } from "react";
import { wakuService } from "../services/WakuService";

export const useWaku = () => {
  const [isConnecting, setIsConnecting] = useState(false);
  const [isConnected, setIsConnected] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const initWaku = async () => {
      if (wakuService.isReady()) {
        setIsConnected(true);
        return;
      }
      setIsConnecting(true);
      setError(null);
      try {
        await wakuService.initialize();
        setIsConnected(true);
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to connect to Waku");
      } finally {
        setIsConnecting(false);
      }
    };
    initWaku();
    return () => {
      wakuService.stop();
    };
  }, []);
  return { isConnecting, isConnected, error };
};
Enter fullscreen mode Exit fullscreen mode

Messaging Flow

Sequence Diagram: Sending and Receiving a Message

Sequence diagram

Core Flow

  • User submits a message via the UI (MessageForm).
  • Hook useMessages calls messageService.publishMessage.
  • MessageService encodes and pushes the message to the Waku network.
  • Node listens for incoming messages and updates UI in real time.

Key Code References

//name=src/services/MessageService.ts 
export class MessageService {
  private getEncoder() {
    const node = wakuService.getNode();
    return node.createEncoder({ contentTopic: CONTENT_TOPIC });
  }
  async publishMessage(content: string): Promise<void> {
    const node = wakuService.getNode();
    const encoder = this.getEncoder();
    const message = { content, timestamp: Date.now() };
    const payload = this.encodeMessage(message);
    await node.lightPush.send(encoder, { payload });
  }
  async subscribeToMessages(onMessage: (message: WhisperMessage) => void): Promise<() => void> {
    const node = wakuService.getNode();
    const decoder = this.getDecoder();
    await node.filter.subscribe([decoder], (wakuMessage) => {
      if (!wakuMessage.payload) return;
      const decoded = this.decodeMessage(wakuMessage.payload);
      onMessage({ id: `${decoded.timestamp}-${Math.random()}`, content: decoded.content, timestamp: decoded.timestamp });
    });
    return () => { /* unsubscribe logic */ };
  }
}
Enter fullscreen mode Exit fullscreen mode

Onboarding: Steps for Beginner Contributors

Getting Started

  1. Clone the Repository
git clone https://github.com/Oladotunlaniyan/whisper-waku-chat.git
cd whisper-waku-chat
Enter fullscreen mode Exit fullscreen mode
  1. Install Dependencies
npm install
Enter fullscreen mode Exit fullscreen mode
  1. Run the App
npm run dev
Enter fullscreen mode Exit fullscreen mode
  1. Edit Code Locally
    • Most logic lives in src/
    • Message logic: src/services/MessageService.ts
    • Waku node logic: src/services/WakuService.ts
    • UI: src/components/, src/pages/

Contribution Guide

  • Follow the onboarding steps above for local development.
  • Make sure you have Node.js and npm installed (guide).
  • Follow the onboarding steps above for local development.
  • Submit issues or pull requests for improvements, bug fixes, or new features.
  • For UI tweaks, edit files in src/components/.
  • For protocol logic, edit src/services/WakuService.ts and src/services/MessageService.ts.

First-Time Contributor Tips

  • Start by reading the README.
  • Use the hooks and services as your entry points for understanding message flow.
  • To add a feature, create a new component in src/components/ and integrate with hooks/services.
  • For debugging, use console logs in services and hooks.
  • Ask for help or suggestions via GitHub Issues.

Visuals

  • System Architecture Diagram (see above)
  • Sequence Diagram for Messaging (see above)
  • UI Screenshot: (Add your own screenshot or generate one from running the app.)

Conclusion

Whisper Waku Chat is a simple but powerful demo of decentralized, anonymous messaging using Waku.

With a modern React frontend and the Waku JS SDK, it’s easy for new developers to get started and contribute.

Next Steps:

  • Fork the repo, run locally, and start hacking!
  • Check out the Waku docs for protocol details.
  • Join the Waku dev community for support and collaboration.

Further Resources

This is a very simple implementation of the Waku protocol, in subsequent articles I will be increasing functionality and complexity of the whisper board. This includes using new methods, explaining more core concepts etc.

Top comments (0)