Expand description

An implementation of phoenix channels for trillium.rs

Channels are a means of distributing events in soft-realtime to connected websocket clients, including topic-subscription-based fanout.

From the phoenix docs,

Some possible use cases include:

  • Chat rooms and APIs for messaging apps
  • Breaking news, like “a goal was scored” or “an earthquake is coming”
  • Tracking trains, trucks, or race participants on a map
  • Events in multiplayer games
  • Monitoring sensors and controlling lights
  • Notifying a browser that a page’s CSS or JavaScript has changed (this is handy in development)
  • Conceptually, Channels are pretty simple.

First, clients connect to the server using WebSockets. Once connected, they join one or more topics. For example, to interact with a public chat room clients may join a topic called public_chat, and to receive updates from a product with ID 7, they may need to join a topic called product_updates:7.

Clients can push messages to the topics they’ve joined, and can also receive messages from them. The other way around, Channel servers receive messages from their connected clients, and can push messages to them too.

Current known differences from phoenix channels:

No long-polling support

Phoenix channels support long polling transports as well as websockets. As most modern browsers and http clients support websockets, as of the current version, trillium channels exclusively are built on them. The design should be flexible to support long polling if it is eventually needed.

No multi-server synchronization yet

Phoenix channels support running several server nodes and distributing all broadcast messages between them. This will be straightforward to add to trillium channels in a future revision, but the current implementation does not synchronize messages across servers. However, in the mean time, you can use Channel::broadcaster to return a ChannelBroadcaster that can be used to publish and subscribe to messages between servers using whatever distribution mechanism is appropriate for your application and deployment. Open a discussion on the trillium repo for ideas on how this might work for you.

Event routing is handled in user code

Phoenix channels has a notion of registering channel handlers for different topics, so an implementation might involve registering a RoomChannel for rooms:*. Trillium channels does not currently provide this routing/matching behavior, but will likely do so eventually.

Simple Example: Chat App

use trillium_channels::{channel, ChannelConn, ChannelEvent, ChannelHandler};

struct ChatChannel;
impl ChannelHandler for ChatChannel {
    async fn join_channel(&self, conn: ChannelConn<'_>, event: ChannelEvent) {
        match event.topic() {
            "rooms:lobby" => {
                conn.allow_join(&event, &()).await;
                conn.broadcast(("rooms:lobby", "user:entered"));

            _ => {}

    async fn incoming_message(&self, conn: ChannelConn<'_>, event: ChannelEvent) {
        match (event.topic(), event.event()) {
            ("rooms:lobby", "new:msg") => conn.broadcast(event),
            _ => {}

// fn main() {
//     trillium_smol::run(channel(ChatChannel));
// }

See channels/examples/channels.rs for a fully functional example that uses the front-end from the phoenix chat example.


  • This macro provides a convenient constructor for a ChannelEvent. It is called with a topic, an event, and an optional inline json payload.
  • Construct a serde_json::Value from a JSON literal.



  • The phoenix channel “protocol” version