Files
KoalaSync/docs/host-control-mode.md

152 lines
3.8 KiB
Markdown

# 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:
- `play`
- `pause`
- `seek`
- `force_sync_prepare`
- `force_sync_execute`
- `episode_lobby`
- `episode_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:
```js
{
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:
- `everyone`
- `host-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`;
- `controlMode` falls back to `everyone`;
- `controllers` is 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-only` and
sends `HOST_BLOCKED` to 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-control`
- `co-host`
The extension hides or disables matching UI when capabilities are missing.
## Related Events
See [PROTOCOL.md](PROTOCOL.md) for payloads and relay behavior for:
- `set_control_mode`
- `control_mode`
- `set_peer_role`
- host-only gated relay events