Waku Store: Reliable Offline Messaging Without a Central Server
Waku Store lets late-joining nodes retrieve message history from peer-run logs instead of a central server. Here's how it works, how to verify it locally, and what it costs.
03 Dec 2025, 09:34 UTC

The problem: messages disappear when the sender goes offline
If you build a chat or notification app on a gossip protocol like libp2p pubsub, you inherit a hard constraint: a message only reaches peers that are subscribed at the moment it is published. A user who opens the app at 9 a.m. sees nothing from 8:55 p.m. the night before. The usual fix is a central message server, but that reintroduces the single point of failure and custody problem that decentralized protocols are trying to avoid.
Waku Store is Waku's answer to this. It is a persistence layer where designated peers keep a local log of messages, so a node that comes online later can ask the network for history instead of relying on a server you operate. This post explains how it works, walks through a local verification setup, and covers the trade-offs you should plan for before depending on it.
How Waku Store is structured
Waku is a family of messaging protocols built on libp2p. The relay layer uses gossip to propagate live messages, organized by content topics — string labels (for example /myapp/1/chat/proto) that let subscribers filter what they care about. Store sits alongside relay as a separate protocol.
A store-enabled peer keeps a simple key-value log, roughly timestamp → message, indexed by content topic. When another node wants history, it sends a request-response query specifying filters such as content topic, time range, or payload hash, and the store peer replies with matching messages. Because any number of peers can run the store protocol, the data is replicated across the overlay rather than held in one place. The result is eventual consistency with no central server: as long as at least one store peer saw and kept the message, a late-joining node can retrieve it.
Two properties follow from this design. First, persistence is opt-in — only nodes started with store support retain history, so the durability of a topic depends on how many store peers serve it. Second, store is a retrieval mechanism, not a delivery guarantee: it complements live gossip rather than replacing it.
Interacting with the store over JSON-RPC
When running a Waku node (for example nwaku, the Nim implementation) with its REST/RPC interface enabled, the typical workflow looks like this:
- A publisher sends a message to a content topic via the relay/publish RPC.
- Store-enabled peers that receive it persist it in their local log.
- Later, any node issues a store query against a connected store peer, filtering by content topic and optionally a time window, and receives the matching messages.
The exact RPC method names and field shapes differ between node implementations and versions, so check the API docs for the version you run rather than relying on memorized method names. The conceptual query — "give me messages for topic T between timestamps A and B" — is stable across implementations.
A worked example: verifying store behavior locally
You can confirm the offline-retrieval property with two nodes on one machine. The following is a sketch of the procedure, not a copy-paste script — flags and ports vary by release, so treat it as a checklist and consult your version's --help output.
- Start node A with store support enabled (commonly a
--store-style flag) and an RPC/REST port, e.g.--rpc-address=127.0.0.1 --rpc-port=8545. You need permission to bind local ports; no elevated privileges are required. - Start node B with a different libp2p listen port and RPC port, and connect it to node A (via an explicit peer address or a discovery mechanism your build supports).
- From node B, publish a message with a recognizable payload (e.g.
hello-store-42) to a custom content topic using the publish RPC. - Wait a few seconds for gossip to reach node A, then shut node B down entirely.
- Start node C (or reuse B's identity after restart), connect it to node A, and issue a store query for the same content topic covering the publish time window.
- Check that the returned message's payload decodes to
hello-store-42. That is the core assertion: the message survived the publisher going offline.
To be sure the answer came from the store overlay and not from live gossip, enable debug logging on the querying node and look for store-protocol request/response traffic. If the publisher is still connected when you query, you have not actually tested persistence — the disconnection step is the whole point.
One caveat: this local setup proves the mechanism on a single store peer. It says nothing about behavior with multiple store peers, clock skew between nodes, or large histories, all of which you should test separately before shipping.
Trade-offs you need to plan for
Storage growth. Every store peer keeps what it sees until something removes it. On busy topics, disk usage grows with message volume times retention period. Production operators need a pruning policy — time-based or size-based retention — or an external storage backend. Decide this before launch, not after the disk fills.
Availability is probabilistic. A store query only reaches the store peers you are connected to. If few replicas are online, some history may be temporarily unavailable. For applications where history matters, run enough store-capable nodes to make the probability of total absence negligible, and treat queries as best-effort with retries.
Query cost. Broad queries (wide time ranges, popular topics) return large result sets and are typically paginated. Design clients to page through results and to narrow queries by time window rather than fetching everything.
Not a consistency system. Store gives you eventual, per-peer logs. There is no global ordering guarantee across peers beyond message timestamps, so applications that need strict ordering should embed their own sequence information in payloads.
Where to go from here
If your application needs offline-first messaging without operating a central server, Waku Store is a pragmatic building block — but only if you treat persistence as a resource you provision and prune, not a free property of the network. The most useful next step is the two-node experiment above: it takes minutes, and seeing a query succeed after the publisher disconnects will tell you more about the model than any diagram. From there, load-test a store peer with realistic message volume and measure disk growth against your retention policy before committing to it in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.