Choosing Waku's Messaging Layer for Decentralized Chat: Relay, Store, or Light Push
Waku offers Relay for live gossip, Store for durable history, and Light Push for thin clients. This guide compares the layers, explains trade-offs, and shows how to validate each path.
30 Aug 2025, 01:34 UTC

When you build a decentralized chat application on Waku, the first hard decision is not the UI framework or the cryptography—it is which messaging layer carries your messages. Waku offers three distinct paths: Relay for live gossip, Store for durable history, and Light Push for thin clients that cannot maintain a full mesh. Choosing the wrong layer means either losing every message when a user goes offline or paying for infrastructure your product does not need. This guide compares the layers, explains the trade-offs, and shows how to validate each path against your target Waku version.
The decision: liveness, durability, or client weight
Decentralized chat forces a three-way tension. Users expect messages to appear instantly (liveness), they expect to scroll back to yesterday (durability), and many of them are on phones or browsers that cannot sustain a libp2p gossip mesh (client weight). Waku splits these concerns across separate protocols rather than forcing a single compromise.
- Relay is a push/subscribe protocol over libp2p gossip. It delivers messages in real time but keeps no history. If a peer is offline when a message is gossiped, it misses that message.
- Store adds persistent indexing. A Store node records messages by content topic so clients can query history later. The cost is extra infrastructure and higher retrieval latency compared to live gossip.
- Light Push lets a lightweight client publish through a relay node without joining the full gossip mesh. It trades bandwidth and some censorship resistance for the ability to send messages from constrained environments.
A fourth concept, content topics, is orthogonal: it is a filtering mechanism that works on both Relay and Store so nodes only process messages relevant to a room or channel.
Comparing the supported options
| Layer | Delivery model | Persistence | Typical latency | Operational cost | Best fit |
|---|---|---|---|---|---|
| Relay | Gossip pub/sub over libp2p | None | Low | Moderate: run or trust relay peers | Ephemeral chat, presence, live typing |
| Store | Indexed history via Store nodes | Yes | Higher than Relay | High: run or trust Store nodes | Scrollback, search, cross-device sync |
| Light Push | Publish via relay nodes | None at client | Low to medium | Low: no mesh maintenance | Mobile, browser, IoT clients |
Trade-offs you will actually feel
Bandwidth and battery. A full Relay mesh means maintaining connections to multiple peers and forwarding messages. On mobile networks this is expensive. Light Push removes the mesh-maintenance burden, but it introduces a dependency: your message only reaches the network if the relay node you publish through is reachable and honest.
Latency. Relay is the fastest path because it is push-based. Store retrieval is slower because it is a query against an index. If your UX requires instant delivery, use Relay for the live path and Store only for history.
Trust and censorship resistance. If you rely on a third-party Store node, that operator can choose not to index your messages or can serve incomplete history. If you rely on a third-party relay for Light Push, that operator can censor your outbound messages. For censorship-resistant guarantees, run your own Relay and Store nodes in the same cluster and shard as your traffic.
Message filtering. Content topics such as /my-app/1/room-alpha/proto let you subscribe to exactly the rooms you care about. Without them, a Relay node must process every message in the shard, wasting CPU and bandwidth.
Concrete implementation: a Relay subscription with content-topic filtering
The following pattern is illustrative for a Waku v0.x JavaScript environment. Exact export names and constructor options change between releases, so verify them against the version you install.
Run the commands below in a Node.js project directory. No elevated operating-system permissions are required, but the process needs outbound network access.
# Terminal / shell
npm init -y
npm install @waku/core @waku/utils
// File: relay-demo.mjs
// Run with: node relay-demo.mjs
// Requires: network access; no privileged ports
// Import paths and function names vary by release; verify against your installed version
import { createRelayNode, createDecoder, createEncoder } from '@waku/core';
const CONTENT_TOPIC = '/my-chat/1/room-alpha/proto';
async function main() {
const node = await createRelayNode({
clusterId: 1,
shards: [0],
});
const decoder = createDecoder(CONTENT_TOPIC);
const encoder = createEncoder(CONTENT_TOPIC);
await node.filter.subscribe([decoder], (message) => {
console.log('Received:', new TextDecoder().decode(message.payload));
});
await node.lightPush.publish(encoder, {
payload: new TextEncoder().encode('hello room'),
});
}
main();
Expected check: the subscriber callback should fire with the payload you published. If it does not, confirm that the node reached the relay network and that the content topic string matches exactly on both sides. Risk: because Relay has no persistence, a message published while the subscriber is offline is lost.
Concrete implementation: durable history with a Store node
To retain history, run a Store node in the same cluster and shard as your Relay traffic. The Store node indexes messages by content topic. Clients then query the store instead of relying on live gossip alone.
The following command is a conceptual example for running a local Store-capable node. Replace the image and tag with the Waku node image for your target release.
# Terminal / shell
# Run in: any directory with Docker available
# Permissions: outbound network; default ports only
docker run -p 60000:60000 <image>:<tag> \
--cluster-id=1 \
--relay=true \
--store=true
After the node is running, publish a message through Relay as shown above, then query the store with a content filter:
// Illustrative query pattern; verify API for your version
const history = await node.store.query({
contentTopics: [CONTENT_TOPIC],
// time or pagination filters per release
});
for (const msg of history) {
console.log(new TextDecoder().decode(msg.payload));
}
Expected check: the query should return the message you published earlier, even if the client restarted between publish and query. Risk: a third-party Store operator can withhold or reorder history. Run your own node if that is unacceptable.
When Light Push is the right call
Use Light Push when your client is a browser tab, a mobile app, or any environment where maintaining a libp2p gossip mesh is impractical. The client publishes through a relay node and subscribes to Relay for incoming messages. You trade bandwidth and some independence for the ability to participate without running a full node. If your product depends on outbound censorship resistance, Light Push alone is insufficient; you need a full Relay mesh or a trusted relay you control.
Validation checklist
Before you commit to a layer, run these checks against your target Waku version:
- Relay liveness: subscribe to a content topic, publish a test message, and confirm the callback fires within a few seconds.
- Store durability: publish a message, restart the client process, query the Store node, and confirm the message is returned.
- Latency comparison: run a simple benchmark script that measures round-trip time for Relay-only delivery versus Store-backed retrieval under identical network conditions.
- Topic isolation: subscribe to topic A, publish to topic B, and confirm that no cross-delivery occurs.
Limitations and version sensitivity
Waku v0.x releases have changed protocol details, default ports, and JavaScript API surface. Pin your dependency versions and re-run the validation checklist whenever you upgrade. Content-topic formats are strings, but their semantics are enforced by your application, not by the protocol. Finally, no layer provides end-to-end encryption by itself; if your chat requires confidentiality, layer a payload encryption scheme on top of the messaging layer you choose.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.