Docs

Realtime

Audio, video, screen share, and participant state go through LiveKit. Light collaboration (chat, raise hand, reactions) uses a LiveKit data channel.

Token flow

text
POST /api/livekit/token
   ↓
Same-origin check + rate limit
   ↓
Authorize actor (member / guest cookie)
   ↓
Schedule + visibility + capacity
   ↓
Mint short-lived JWT (TTL capped by endTime)
   ↓
Return token + LIVEKIT_URL to client

Identities are set server-side: user:{uuid} or guest:{roomId}:{guestId}. Clients cannot pick their own identity in the request body.

Long meetings soft-refresh the token before JWT expiry (about 90s early) so the connection can continue without a full remount when possible.

In the meeting UI

  • Camera and microphone toggles
  • Screen sharing
  • Participant grid / screen-share stage
  • Speaking indicators from LiveKit
  • Chat, raise hand, emoji reactions (data channel)
  • Connection banner for reconnect / disconnect
  • Host/moderator mute, disable camera, remove

Why not Socket.IO?

Meeting-scoped signals already ride LiveKit’s data channel. Adding a second realtime bus would mean another service to run and secure. Application events that need durability (orders, memberships) stay in Postgres and Server Actions.