Realtime
Push it to everyone
who is listening
Chat, presence, notifications, a dashboard that updates itself. One publish, many subscribers, no polling. And in the browser build, the same pub/sub works between two tabs with no server at all.
Why this fits — and where it does not
Pub/sub is fire-and-forget. A message goes to whoever is subscribed at that moment; nobody who connects a second later will ever see it, and there is no acknowledgement. That is exactly right for a presence ping or a live counter, and exactly wrong for anything you would be upset to lose.
If losing a message matters, use a stream instead — see queues. Streams keep history, support consumer groups, and let a client that was offline catch up. Pub/sub is the cheap thing; the cheapness is the trade.
How
# subscriber
SUBSCRIBE room:42
PSUBSCRIBE room:* # every room, one connection
# publisher — returns how many subscribers received it
PUBLISH room:42 '{"user":"ada","text":"hello"}'
-> (integer) 3
# presence: the TTL does the expiry, the client refreshes every 10 s
SET presence:ada online EX 30
# who is here. on a large keyspace prefer a set:
SADD online ada
SMEMBERS online
SREM online adaThe same thing, in a browser tab
import { open } from "@goliajp/kevy";
const db = await open({ persist: { name: "app" } });
// tab A
db.subscribe("room:42", (payload, channel) => {
render(JSON.parse(new TextDecoder().decode(payload)));
});
// tab B — tab A receives it
db.publish("room:42", JSON.stringify({ user: "ada", text: "hello" }));A slow subscriber is dropped, not buffered forever. If a client cannot keep up, its messages are discarded rather than growing the server's memory without bound — a deliberate choice, and one you should know about before you rely on delivery. There is no acknowledgement and no replay. If either matters, you want a stream, not a channel. The pub/sub guide is specific about the limits.