3.8 KiB
Host Control Mode
This document describes the Host Control Mode implementation in the relay and extension. It only covers behavior implemented in the current codebase.
Modes
everyone
- Default room mode.
- Any peer may send room-moving playback events.
host-only
- Only controllers may send room-moving playback events.
- The host is always a controller.
- The host can promote additional peers to controllers.
- Guests can keep watching locally in solo/desynced mode, but their local actions still do not drive the shared room.
Room-moving events are:
playpauseseekforce_sync_prepareforce_sync_executeepisode_lobbyepisode_lobby_cancel
Heartbeats, force-sync ACKs, episode-ready events, ping/pong, and command ACKs remain allowed for guests.
Server State
Rooms store Host Control state in memory:
{
hostPeerId,
controlMode,
controllers,
lastControlModeChangeAt,
lastRoleChangeAt,
forceSyncInitiator
}
controllers is a Set on the server and is serialized as an array in
room_data and control_mode.
State is not persisted across relay restarts.
Authority Rules
Changing mode
Only hostPeerId may send set_control_mode.
Valid values:
everyonehost-only
Invalid values are ignored. Non-host attempts are ignored and the sender receives
the current control_mode snapshot so optimistic UI can revert.
Mode changes are debounced per room for 500 ms.
Promoting and demoting controllers
Only hostPeerId may send set_peer_role.
The host cannot demote themself. No-op role changes are ignored. Role changes are debounced per room for 500 ms.
Host leaving
When the host leaves and peers remain:
- the next peer becomes
hostPeerId; controlModefalls back toeveryone;controllersis reset to the new host;- the relay broadcasts
control_mode.
When a non-host controller leaves, the relay removes that peer from
controllers and broadcasts control_mode.
Enforcement
The implementation has two enforcement points:
- The extension background script blocks local guest attempts in
host-onlyand sendsHOST_BLOCKEDto the content script for local UX. - The relay drops room-moving events from non-controllers in
host-only, so old or modified clients cannot drive the room.
The relay is the authority for room-wide effects.
Guest UX
When a guest action is blocked locally, the content script classifies it:
- deliberate user action: show the host-control dialog;
- likely involuntary player action (buffering, tab refocus, no recent gesture): silently snap back when safe;
- live/DVR stream: degrade without forcing snap-back.
The dialog offers:
- stay in sync: resync to the host;
- watch on my own: enter solo/desynced mode.
In solo/desynced mode:
- the guest can control their local video;
- host room commands are ignored locally, except force-sync preparation is ACKed so the host's flow can continue;
- the guest can resync to the host.
The extension reports desynced in peer status so the host UI can show that a
guest is watching solo.
Force Sync Edge Case
The relay tracks forceSyncInitiator after a controller sends
force_sync_prepare.
This allows that same initiator's force_sync_execute through even if their
controller role changes before execute arrives. Without this, a demotion in the
middle of a force-sync flow could leave peers waiting after prepare.
The relay clears forceSyncInitiator after execute or when the initiator leaves.
Capabilities
The relay advertises Host Control support in room_data.capabilities:
host-controlco-host
The extension hides or disables matching UI when capabilities are missing.
Related Events
See PROTOCOL.md for payloads and relay behavior for:
set_control_modecontrol_modeset_peer_role- host-only gated relay events