Skip to main content

Live classes and recording

Live classes and mentoring sessions use LiveKit. Three pieces run in the cluster:

  • LiveKit server (livekit-server, image livekit-server:v1.9.1 when observed). It hosts the rooms. Browsers reach it at livekit.rtih.peoplewave.in over WSS. Media uses its own load balancer for TCP and UDP.
  • LiveKit egress (livekit-egress, a custom image named lms-egress). It joins a room, composes the video and writes an MP4 to the bucket.
  • LMS service. It creates classes, mints join tokens, starts and stops egress, and receives the webhooks.

Automatic recording​

A class is recorded for the whole time it is in session. Nobody presses record. The recorder starts when the first track is published, not when the class starts, because an egress started in an empty room never signals that it has started and aborts with code 412.

  1. A mentor starts the class. The status becomes IN_PROGRESS and the recording status stays NONE.
  2. A participant publishes a track. The LiveKit server sends a track_published webhook to the LMS service.
  3. The LMS service verifies the webhook, finds the class from the room name (class-<classId>), and starts a room-composite egress. The recording status becomes RECORDING. This step is idempotent: a class that already has an egress id does not start a second one.
  4. When the class ends, the LMS service stops the egress. The recording status becomes PROCESSING.
  5. The egress writes recordings/<classId>/<uuid>.mp4 to the bucket, and the server sends an egress_ended webhook with status EGRESS_COMPLETE.
  6. The LMS service creates an LmsRecording row and sets the class recording status to READY.

The live-class recording flow shows this as a sequence diagram.

The webhook​

POST /api/v1/lms/live-classes/egress-webhook is a public path. LiveKit signs each webhook with a JWT in the Authorization header, with no Bearer prefix. The JWT is signed with the LiveKit API secret and carries a SHA-256 checksum of the body. LiveKitWebhookVerifier checks the signature and the checksum in constant time. LiveKit sends the body as application/webhook+json, so the endpoint accepts any content type and lets the signature decide.

The server delivers webhooks to http://lms-service:8080/... inside the cluster.

Sources: services/lms-service/src/main/java/com/rtih/startupos/lms/{service/LiveClassService,service/LiveKitWebhookVerifier,resource/EgressWebhookResource}.java, .agents/tasks/lms-egress-412/fix-report.md, infra/k8s/livekit-egress.yaml, infra/oci-devops/deploy/livekit-deployment.yaml, live Deployment images read on 2026-11-07.