Ivan FB fd22c7d8d7
feat: add the channel-chat example
A standalone room-chat TUI on the reliable channels API, with no MLS or
libchat stack in the way — the channels API on its own. One process per
participant (--nick/--room), --peer to wire two local instances directly or
logos.dev discovery otherwise. The author's nick travels inside the payload
because a received event's sender_id reports the receiving channel's own
participant id, not the originator's.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ai43sF2rymPFMG9iki9fwL
2026-07-18 17:31:28 +02:00

3.2 KiB

channel-chat

A room chat built directly on logos-delivery's reliable channels API, via waku-bindings. It is deliberately small: no MLS, no libchat stack, no encryption of the chat itself — just the channels API, so the API is the only thing on screen.

For the full encrypted client see bin/chat-cli, which uses these same channels underneath as its transport.

Running

One process per participant, each with its own nick:

cargo run -p channel-chat -- --nick Alice --room lobby   # terminal 1
cargo run -p channel-chat -- --nick Bob   --room lobby   # terminal 2

Type and press Enter to send; Esc quits. The node takes a few seconds to start and find peers on logos.dev, so the first message may need a retry. Each instance prints its own multiaddr at startup; to wire two local instances together without waiting on discovery, pass one to the other:

cargo run -p channel-chat -- --nick Bob --room lobby --peer /ip4/127.0.0.1/tcp/60001/p2p/16Uiu2...
Flag Default Meaning
--nick (required) Display name, and the channel's SDS participant id
--room lobby Room to join — everyone in a room shares one channel
--port 0 (OS picks) TCP port for the node
--peer (none) Multiaddr to dial directly, instead of waiting on discovery

What it does

  1. Starts a node on the logos.dev preset. The preset is what supplies the entry nodes and autosharding — a channel's shard is derived from its content topic, so a node without autosharding cannot carry one.
  2. Registers the three channel listeners — received, sent, errorbefore starting the node.
  3. Opens channel {room} on content topic /logos-chat-example/1/{room}/proto.
  4. Subscribes to that content topic. Creating a channel does not subscribe; ingress arrives through the messaging layer, so without this nothing lands.
  5. Sends with channel_send, and closes the channel on quit.

Notes

  • Run each participant under a different --nick. The nick is both the participant id and (via ./data-channel-chat-{nick}) the storage root. The persistency layer is a process-wide singleton keyed on that root, and SDS rows are keyed by channel id, so two participants sharing a root would each load the other's causal history and silently drop the other's messages as replays. This is also why participants are separate processes rather than tasks.
  • Sends are acknowledged asynchronously, so the outcome shows up in the status bar (sent / SEND FAILED) rather than at the call site. That reliability — acks, retransmission, causal ordering — is what channels add over a raw relay publish.
  • A node does not receive its own channel messages, so your own lines are echoed locally.
  • The author's nick travels inside the payload, not in sender_id. A received event's sender_id is filled in from the receiving channel's own participant id (reliable_channel.nim's reportReceived emits self.senderId), so reading the author off it would attribute every incoming message to whoever is reading it. Worth knowing before reaching for that field.