Channel for WebRTC signaling relay.
Topic: signaling:<room_id>
A room is a lobby: Gamend.Signaling derives everything from the lobby's
server-owned webrtc_* columns, so this channel keeps no membership list of
its own and nothing here can drift from the lobby.
Lifecycle
On join the authenticated user_id is used directly as the user identity, and
Signaling.authorize/2 decides both whether the join is allowed and which
role it gets — :host or :user. Clients cannot ask for a role. In a :star
room the host is lobby.webrtc_host_id, or lobby.host_id when that is
unset; a :mesh room has no host.
If the same user_id reconnects within the configured grace period, the
existing user is preserved and a user_rejoined event is broadcast.
Messages
Inbound events (from client):
push("offer", %{target: "user-uuid", sdp: "..."})
push("answer", %{target: "user-uuid", sdp: "..."})
push("ice", %{target: "user-uuid", candidate: "..."})
push("broadcast_offer", %{sdp: "..."}) # star host only
push("list_users", %{})Outbound events (to client):
"offer" — %{sdp: "...", from_user_id: "..."}
"answer" — %{sdp: "...", from_user_id: "..."}
"ice" — %{candidate: "...", from_user_id: "..."}
"user_joined" — %{user_id: "...", role: :host | :user}
"user_rejoined" — %{user_id: "...", role: :host | :user}
"user_left" — %{user_id: "..."}
"room_closed" — %{}