The video room moderation page says poll answers have stopped while 186 dispatch trainees remain connected. One participant is talking over the route-planning screen share, and the console shows three attempts at muting them. The least complex safe response is to mute first, verify room state, and consider kicking only when disruption persists or authorization is revoked.
TL;DR: muting preserves access to the screen share and live poll; ejection removes room access and requires readmission. Model both as idempotent state transitions. At fan-out, retries must converge on the newest desired state rather than revive an older command.
Should video room moderation start by muting or kicking participants?
The useful page identifies the session, poll, participant, desired state, last confirmed state, and age of that confirmation. It separates media disruption from poll health. A muted microphone can be correct while answers keep arriving; a flat answer counter may mean the shared question is unreadable or the poll channel is unhealthy.
| Action | See screen share | Answer poll | Re-entry | Use |
|---|---|---|---|---|
| Mute | Yes | Yes | No | Noise or first disruption |
| Eject | No | No | Yes | Repeated disruption or revoked access |
WebRTC specifies peer connections, media senders, receivers, and transceivers. Room membership and moderation policy remain application concerns. A local UI mute is therefore not proof that every listener stopped receiving audio; the authoritative service must distribute desired state and observe confirmation. The tradeoffs are asymmetric: muting has a smaller blast radius but leaves chat, poll, and viewing access intact, while kicking participants contains more behavior at the cost of interrupting legitimate work and complicating recovery.
Work backward from the late signal
Poll-answer loss is user impact, but it arrives late. Earlier signals are commands older than the delivery objective, subscribers that have not acknowledged the current version, and commands superseded before confirmation.
Suppose mute version 41 is delayed, then an operator ejects the participant with version 42. If 41 arrives last, arrival-order processing weakens the final state. Store the highest version for each session and participant, reject older deliveries, and reuse a command ID for retries.
package moderation
import "sync"
type Command struct {
ID, SessionID, ParticipantID string
Version uint64
Desired string
}
type Store struct {
mu sync.Mutex
version map[string]uint64
state map[string]string
}
func (s *Store) Apply(c Command) bool {
key := c.SessionID + "/" + c.ParticipantID
s.mu.Lock()
defer s.mu.Unlock()
if c.Version <= s.version[key] {
return false
}
s.version[key], s.state[key] = c.Version, c.Desired
return true
}
The map exposes the invariant; production storage needs an atomic, durable comparison. Duplicate delivery is routine. No drama.
Run the failure exercise as an event trace, not as a happy-path unit test. At 09:14:00, the moderator sends mute version 41. One subscriber confirms it, another loses its connection, and the command dispatcher retries. At 09:14:08, policy sends eject version 42 because the participant's authorization has been revoked. Deliver 42 first to the reconnected subscriber, then deliver both copies of 41. The subscriber must remain ejected and acknowledge 42. Next, make the acknowledgement for 42 disappear while the state change succeeds. The dispatcher should retry 42 with the same command ID, and applying it again should produce no second side effect. Finally, replay the entire trace after a process restart. If durable state was implemented correctly, the outcome is unchanged. This exercise finds the awkward gap between at-least-once command delivery and exactly-once business intent without pretending the network provides exactly-once transport. It also gives the on-call a recognizable timeline: desired version advanced, confirmation lagged, retry occurred, and state converged.
Instrument the transition, not the click
Record command ID, bounded reason, session and participant IDs, desired state, version, creation time, confirmation time, attempts, and outcome in an audit event. Keep spoken content and poll answers out of metric labels. They increase cardinality and privacy exposure without improving the page.
Measure command age, confirmation latency, delivery attempts, stale-version rejections, and the count of participants whose desired and confirmed states disagree. Correlate those with connected participants and answer throughput in logs or traces. A click counter measures intent; it cannot close an incident.
package moderation
import "time"
type Transition struct {
CommandID, Desired, Outcome string
Version uint64
CreatedAt, ConfirmedAt time.Time
}
func (t Transition) Latency() (time.Duration, bool) {
if t.ConfirmedAt.IsZero() || t.ConfirmedAt.Before(t.CreatedAt) {
return 0, false
}
return t.ConfirmedAt.Sub(t.CreatedAt), true
}
Close the loop only when authoritative state is confirmed and fan-out targets have acknowledged that version or expired under a documented policy.
Mute is lower impact because the trainee stays in the instructional flow. Ejection is containment. Never downgrade ejected because a stale mute retry appeared, and make reconnect pass admission policy instead of restoring cached membership.
Test reordered and duplicated events. Create mute version 7 and eject version 8, deliver each twice in every ordering, and require ejected as the final state. Verify separately that muting audio leaves the shared track and poll submission available. Then test an old room credential against the readmission rule.
Deploy observation before automated enforcement. Rehearse with a synthetic session, inspect label cardinality, and roll out by stable session cohorts so a room does not change policy halfway through its poll.
This design has limits. It does not fit a room where the application cannot authoritatively control media subscriptions or admission; in that case, the UI can express moderator intent but cannot prove enforcement. Muting is also the wrong control for compromised credentials, while kicking is excessive for an accidental open microphone. Those boundaries are why the runbook preserves a human override instead of converting every repeated sound into automatic removal.
Pay attention to false-positive cost
Page when sustained user impact coincides with moderation state failing to converge. A single delayed acknowledgement belongs on a dashboard; a growing set of unconfirmed commands during a live poll may justify waking someone. Exact thresholds must come from the service objective and measured baseline, not a universal number.
An eager page trains responders to distrust the signal and can prompt unnecessary kicking, removing legitimate participants from the video screen share and poll. Waiting for answer throughput to collapse is too late. Review pages requiring no action beside missed-impact events, then tune duration and affected-participant count together.
Mute to preserve participation, eject to contain access risk, and page when confirmed state fails to converge with user impact. Durable versions prevent regression; acknowledgements prove fan-out.
Further reading
- W3C, WebRTC 1.0: Real-Time Communication Between Browsers: https://www.w3.org/TR/webrtc/
Top comments (0)