Real-Time

The Neutron.Channels module provides Phoenix-style real-time communication with topic-based routing, presence tracking, and Nucleus-backed multi-instance sync.

Channels

Define a channel to handle WebSocket messages by topic:

defmodule MyApp.ChatChannel do
  use Neutron.Channel

  def join("chat:" <> room_id, _params, socket) do
    {:ok, assign(socket, :room_id, room_id)}
  end

  def handle_in("message", %{"body" => body}, socket) do
    room = socket.assigns.room_id
    user_id = socket.assigns.user_id

    {:ok, msg} = Neutron.Nucleus.query_one!(MyApp.Pool,
      "INSERT INTO messages (room, user_id, body) VALUES ($1, $2, $3) RETURNING *",
      [room, user_id, body])

    broadcast!(socket, "message", msg)
    {:noreply, socket}
  end
end

Socket Setup

Mount channels on your router:

defmodule MyApp.Socket do
  use Neutron.Socket

  channel "chat:*", MyApp.ChatChannel
  channel "notifications:*", MyApp.NotificationChannel

  def connect(%{"token" => token}, socket) do
    case Neutron.Auth.verify_token(token) do
      {:ok, user_id} -> {:ok, assign(socket, :user_id, user_id)}
      _ -> :error
    end
  end
end

# In your router
websocket "/ws", MyApp.Socket

Presence

Track who is online in each topic with CRDT-based presence:

defmodule MyApp.ChatChannel do
  use Neutron.Channel

  def join("chat:" <> room_id, _params, socket) do
    Neutron.Presence.track(socket, socket.assigns.user_id, %{
      name: socket.assigns.user_name,
      online_at: System.system_time(:second)
    })

    presences = Neutron.Presence.list(socket)
    {:ok, %{presences: presences}, socket}
  end
end

Clients receive presence_diff events automatically when users join or leave.

Multi-Instance Sync

Use Nucleus PubSub to sync broadcasts across multiple Elixir nodes:

# config/config.exs
config :neutron, :pubsub,
  adapter: Neutron.PubSub.Nucleus,
  pool: MyApp.Pool

# Broadcasts automatically fan out to all connected nodes
broadcast!(socket, "message", payload)

Server-Sent Events

For one-way streaming, use SSE:

get "/events" do
  conn = Neutron.SSE.start(conn)

  Neutron.Nucleus.pubsub(MyApp.Pool)
  |> Neutron.PubSub.subscribe("events:*", fn channel, payload ->
    Neutron.SSE.send(conn, channel, payload)
  end)
end