Skip to main content

Send and Receive Messages in a Reliable Channel

Learn how to send and receive messages with a convenient SDK that provide various reliable functionalities out-of-the-box.

danger

This is an experimental feature and has a number of limitations.

Import Waku SDK

npm install @waku/sdk@latest

Or using a CDN, note this is an ESM package so type="module" is needed.

<script type="module">
import {
createLightNode,
ReliableChannel
} from 'https://unpkg.com/@waku/sdk@latest/bundle/index.js';

// Your code here

</script>

Create a Waku node

Use the createLightNode() function to create a Light Node and interact with the Waku Network:

import { createLightNode } from "@waku/sdk";

// Create a Light Node
const node = await createLightNode({ defaultBootstrap: true });
info

When the defaultBootstrap parameter is set to true, your node will be bootstrapped using the default bootstrap method. Have a look at the Bootstrap Nodes and Discover Peers guide to learn more methods to bootstrap nodes.

Create encoders and decoders

Choose a content topic for your application and create a message encoder and decoder:

import { createEncoder, createDecoder } from "@waku/sdk";

// Choose a content topic
const ct = "/my-app/1/messages/proto";

// Create a message encoder and decoder
const encoder = node.createEncoder({ contentTopic: ct });
const decoder = node.createDecoder({ contentTopic: ct });

You can also use @waku/message-encryption to encrypt and decrypt messages using Waku libraries.

info

In this example, users send and receive messages on a shared content topic. However, real applications may have users broadcasting messages while others listen or only have 1:1 exchanges. Waku supports all these use cases.

Listen for connection status

The Waku node will emit health events to help you know whether the node is connected to the network. This can be useful to give feedback to the user, or stop some action (e.g. sending messages) when offline:

import { HealthStatus } from "@waku/sdk";

node.events.addEventListener("waku:health", (event) => {
const health = event.detail;

if (health === HealthStatus.SufficientlyHealthy) {
// Show to the user they are connected
} else if (status === HealthStatus.MinimallyHealthy) {
// Maybe put some clue to the user that while we are connected,
// there may be issues sending or receiving messages
} else {
// Show to the user they are disconnected from the network
}
});

Create a reliable channel

You need to choose a channel name: it acts as an identifier to the conversation, participants will try to ensure they all have the same messages within a given channel.

const channelName = "channel-number-15"

Finally, each participant need to identify themselves for reliability purposes, so they can confirm others have received their messages.

It is up to you how to generate an id. Every participant must have a different id.

const senderId = generateRandomStringId();

You can now create a reliable channel:

import { ReliableChannel } from "@waku/sdk";

const reliableChannel = await ReliableChannel.create(node, channelName, senderId, encoder, decoder)

The channel will automatically start the Waku node and fetch messages.

Create a message structure

Create your application's message structure using Protobufjs:

import protobuf from "protobufjs";

// Create a message structure using Protobuf
const DataPacket = new protobuf.Type("DataPacket")
.add(new protobuf.Field("timestamp", 1, "uint64"))
.add(new protobuf.Field("sender", 2, "string"))
.add(new protobuf.Field("message", 3, "string"));
info

Have a look at the Protobuf installation guide for adding the protobufjs package to your project.

Listen to incoming messages

The reliable channel will emit incoming messages. To process them, simply add a listener:

reliableChannel.addEventListener("message-received", (event) => {
const wakuMessage = event.detail;

// decode your payload using the protobuf object previously created
const { timestamp, sender, message } = DataPacket.decode(wakuMessage.payload);

// ... process the message as you wish
})

Monitor sync status

The reliable channel provides sync status events to help you understand whether the channel is fully synchronized with all participants. This is useful for showing loading states, alerting users about missing messages, or detecting permanently lost messages.

Understanding sync states

The channel can be in one of two states:

  • synced: The channel is not aware of any missing messages that can still be retrieved. Note that some messages may have been permanently lost (see lost in the status detail).
  • syncing: The channel is aware of missing messages and is attempting to retrieve them.

Status detail structure

Each sync status event includes a StatusDetail object with:

  • received: Number of messages successfully received
  • missing: Number of messages that are missing but may still be retrievable
  • lost: Number of messages considered permanently lost (irretrievable). Messages are marked as lost when they cannot be retrieved within a time constraint.

Listen to sync status events

// Listen for when the channel is fully synced
reliableChannel.syncStatus.addEventListener("synced", (event) => {
const { received, missing, lost } = event.detail;

console.log(`Channel synced: ${received} messages received`);

if (lost > 0) {
// Alert the user that some messages were permanently lost
console.warn(`Warning: ${lost} messages could not be retrieved`);
}

// Hide loading spinner, show "up to date" indicator, etc.
});

// Listen for when the channel is syncing
reliableChannel.syncStatus.addEventListener("syncing", (event) => {
const { received, missing, lost } = event.detail;

console.log(`Syncing: ${missing} messages are being retrieved...`);

// Show loading spinner, "syncing" indicator, etc.
});
danger

When messages are marked as permanently lost, there is currently no automatic recovery mechanism available. You can inform users about the loss, but the messages cannot be retrieved. As we continue to improve the Reliable Channel feature, we may add additional recovery mechanisms in the future.

Send messages

To send messages in the reliable channel, encode the message in a payload.

// Create a new message object
const protoMessage = DataPacket.create({
timestamp: Date.now(),
sender: "Alice",
message: "Hello, World!",
});

// Serialise the message using Protobuf
const serialisedMessage = DataPacket.encode(protoMessage).finish();

Then, send the message and setup listeners so you can know when the message:

  • has been sent
  • has been acknowledged by other participants in the channel
  • has encountered an error
// Send the message, and get the id to track events
const messageId = reliableChannel.send(payload);

reliableChannel.addEventListener("sending-message-irrecoverable-error", (event) => {
if (messageId === event.detail.messageId) {
console.error('Failed to send message:', event.detail.error);
// Show an error to the user
}
})

reliableChannel.addEventListener("message-sent", (event) => {
if (messageId === event.detail) {
// Message sent, show '✔' to the user, etc
}
})

reliableChannel.addEventListener("message-acknowledged", (event) => {
if (messageId === event.detail) {
// Message acknowledged by other participants, show '✔✔' to the user, etc
}
})
Congratulations!

You have successfully sent and received messages over the Waku Network using our reliable protocols such as Scalable Data Sync (SDS) and P2P Reliability.