Skip to main content

Transcript-verified video-only gate

An audio-only room tells every participant the same story at join time: you may talk, and everyone can hear you, but your camera and screen-share stay off until the moderator has proof you are a genuine speaker. The gate that enforces this is the transcript-verified video-only gate — a server-side grant transform that sits between the audio-only room policy and the LiveKit token mint. This page is the integrator’s map of it: why the ceiling exists, what the widen predicate actually checks, where in the request path it runs, and the two hard negatives it is designed never to allow. The channel-level overview is Video: rooms, embeds, recording, and broadcast; this page is the grant-semantics contract behind the audio_only flag those surfaces configure.

1. Participant-grant semantics: a ceiling, never a ladder

Orbit’s video rooms mint an effective grant per participant — the join response’s permissions.can_publish plus a per-source publication allow-list (canPublishSources) baked into the LiveKit JWT. In a full-video room, a publishing tier (panelist, host) gets camera, screen-share, and microphone sources. A subscribe-only tier (viewer, hidden_supervisor, a lobby hold) gets no publish capability at all. The audio-only policy applies a ceiling on top of that: every publisher’s allow-list is clamped down to the audio sources (microphone) at the token mint. The ceiling is authoritative server-side — even a client that ignores the audio_only room-metadata hint cannot publish camera or screen-share video, because the minted JWT itself carries the clamped source list. Two consequences follow from the ceiling model:
  • Audio-only is not widenable in principle. The policy bans camera and screen-share publishing from the whole room; your client cannot talk its way back into video by sending anything extra on join. The only way video returns is the gate’s own widen path, below.
  • The ceiling can never create a publisher. A viewer or hidden_supervisor grant has can_publish: false. Neither the audio-only clamp nor the gate ever flips that to true — they only edit canPublishSources for a participant who was already a publisher. A widening operation on a non-publishing tier is a no-op by construction, the same rule the route-layer per-source override runs: an allow-list narrows a non-publisher, it never widens one.

2. The helper contract: clamp, verify predicate, widen

The gate is a pure transform on the grant — no I/O, the same shape in mirror as in mint, so the decision is deterministic and auditable. Read as three separate clauses, they describe the whole lifecycle:
  • Clamp (before verification) — the grant comes back with the publication allow-list intersected to the un-widened audio tier. Camera, screen-share, and screen-share-audio are all dropped; whoever held only microphone keeps it. If the participant was never a publisher, the grant comes back unchanged — the clamp is a ceiling, not a ladder.
  • Verify predicate — a pure, fail-closed decision on the signed-speaker audio cluster’s end-to-end latency against the caller’s tolerance window. When no tolerance is declared the widener is always admitted; when the latency cannot be read or the value is malformed it is always denied. The gate widens only when this predicate comes back true.
  • Widen (after verification) — exactly camera and screen_share are unioned onto whatever sources the publisher already holds. The audio-leg source is never added, non-video sources already present are preserved, and no subscriber is ever promoted into a publisher. The two video sources are the only thing the gate can ever widen; nothing else moves.
The widen is deliberately video-exclusive. Because the audio-only ceiling already grants microphone, the gate’s own source vocabulary excludes it — there is nothing to add there — and excludes the screen-share audio leg for the same reason the audio-only ceiling does: letting it in would re-open a policy the room explicitly banned.

3. Where the clamp applies upstream: the route layer, not a toggle

The route layer applies the gate before the access token is minted — at grant materialization, not at media runtime. The flow on POST /video/rooms-scheduled/:id/join looks like this:
  1. The base tier’s grant is resolved from the requested participant_tier (after host downgrade or lobby hold).
  2. The audio-only room policy clamps the publisher’s source list down to the audio ceiling.
  3. The transcript-verified gate re-clamps to the un-widened tier or unions the two video sources depending on the verify predicate.
  4. The resulting grant is minted into the LiveKit JWT — the SDK client reads the same clamped or widened source list from the token.
This is handler behavior, not an operator toggle. There is no switch on the room or the tenant that turns the gate off independently of audio_only; the gate only ever runs inside the audio-only path, and the moderator’s widen decision is a per-participant call made on the room’s transcript-cluster signal, not a configuration knob. An integrator never “enables the gate” — they enable audio_only on the room, and the gate is the only mechanism by which video comes back afterward.

4. Why widen can never promote to publish

The same non-widening invariant that the route layer’s per-source override enforces holds here: an allow-list can narrow a grant, never widen a subscribe-only one. Concretely:
  • A viewer join with a canPublishSources override is returned unchanged — the override cannot convert them into a publisher.
  • A viewer join verified by the transcript gate is likewise returned unchanged — the gate cannot convert them into a publisher either.
  • The gate only ever moves the two video sources in or out of an already-publishing participant’s canPublishSources. It is structurally incapable of flipping can_publish from false to true.
For the integrator designing a room, this means the gate’s widen verb is safe to call on the whole room’s joined set: nobody not already allowed to publish can gain publish from it. The full participant-tier matrix itself lives in The video room model.

5. Two negative-test examples

The gate’s two hard negatives are what it is designed to guarantee: Audio-only with no transcript — nothing widens.
A panelist joins an audio_only: true room before any signed-speech cluster converges. The gate’s clamp leaves them on the audio ceiling: can_publish stays true, and the publication sources stay exactly ["microphone"]. Camera, screen-share, and screen-share-audio never appear. They stay audio-only until verification. Audio-only with a verified transcript — video, not publish.
The same panelist once the signed-speech cluster verifies them. The gate unions camera and screen_share onto the audio sources — the pre-verification microphone source is preserved — but can_publish was already true; the gate added video sources, not publish capability. A subscribe-only tier (viewer, hidden_supervisor, a lobby hold) run through the same verified gate gets back its original grant unchanged: it upgrades to video-only, never to publish.

Video: rooms, embeds, recording, and broadcast

The channel-level page — room kinds, join tokens, the embed, recording, moderation, and broadcast.

Video meetings and conferences

The ordered walkthrough — create a scheduled room, invite guests, run the meeting end to end.

The video room model

Scheduled vs ad-hoc rooms, the created-to-closed lifecycle, and join-token semantics this gate composes over.

Video room access tokens

Full token scope — participant_tier, effective-grant read-back, and expiry semantics.