Think of the recording feature like an iceberg — the heavy, hard part (the engine) is already underwater and built. What we're doing now is building the visible tip: the controls, the playback screen, the access gates, and the security locks.
recording_worker.py handles start/stop, splits the session into small video chunks (~7 sec each), and uploads them automatically.m3u8 file) that lets videos play smoothly on any deviceBelow is a complete map of the recording feature — from the moment an admin clicks "Start Recording" to the moment a student watches it. Green boxes are already built. Red boxes are what we're building now.
We're using a Hybrid Model (Batch + Admin Control). Think of it in two layers: an automatic layer (everyone in the class gets access without any manual work) and an override layer (admin can add or remove specific people).
Every student currently in the batch linked to that meeting automatically gets access to its recording — no admin action needed.
Admin can add any specific person (by email) to a recording — even if they're not in the batch. Useful for makeup sessions or guest access.
Admin can block a specific student even if they are in the batch. Revoke always wins — it overrides the batch membership.
Every time a user tries to open a recording, the system runs through these checks in order:
| Step | Check | Outcome |
|---|---|---|
| 1 | Is the user logged in? | No → Redirect to login |
| 2 | Is the user an Admin? | Yes → Full Access |
| 3 | Is the user the assigned Teacher for this batch? | Yes → Access Granted |
| 4 | Has the recording's access expiry date passed? | Yes → Access Denied |
| 5 | Is the user in the Revoked list? | Yes → Denied (overrides grant/batch) |
| 6 | Is the user in the Manually Granted list? | Yes → Access Granted |
| 7 | Is the user a member of the batch linked to this meeting? | Yes → Access Granted |
| 8 | None of the above match | 403 — Access Denied |
| Capability | Admin | Teacher | Student |
|---|---|---|---|
| Start / Stop a recording | Yes | Own meetings | No |
| See list of recordings | All meetings | Own meetings | Assigned only |
| Watch / stream a recording | Yes | Yes | If access granted |
| Download a recording | Yes | No (configurable) | Never |
| Delete a recording | Yes | No | No |
| Grant / Revoke access | Yes | No | No |
| Set access expiry date | Yes | No | No |
Medical education content is valuable. We protect it with 4 independent layers — even if a determined person bypasses one layer, the others remain. Think of it like a vault with four locks.
Every video chunk is encrypted before it's stored in Firebase. Even if someone downloads all the raw video files, they are completely unplayable — like having a safe but no combination. The decryption key is only handed out to authenticated, authorised users through our secure backend.
Blocks: direct file downloads · ffmpeg ripping · URL sharing · audio-only extraction tools
CSS and JavaScript tricks that disrupt how screen-recording tools capture the video frame. When the browser detects a screen capture is happening, playback pauses automatically and a warning overlay appears. Full detail in Section 5.
Blocks: OBS, ShareX, browser screen share, QuickTime, Windows Game Bar (~60–80% effectiveness)
Every student sees their own name and masked email address moving slowly across the video (semi-transparent). If they screen-record it anyway, their identity is baked into every frame. When a pirated copy surfaces, we can immediately trace exactly which account it came from.
Watermark injected server-side — cannot be removed by inspecting JavaScript
All video links automatically expire after 90 minutes. If a student copies a link and sends it to a friend, by the time the friend opens it, the link is dead. Every playback session also requires an active login — no login, no video. Every key request is rate-limited and logged with the user's IP address.
Blocks: link sharing · URL scraping · automated key harvesting bots
| Technique | What It Stops | Effectiveness | Included? |
|---|---|---|---|
| AES-128 Encryption | Raw file download, ffmpeg, audio ripping | Very High | Yes — Day 2 |
| CSS Capture Disruption | OBS, ShareX, screen recorder apps | ~60% | Yes — Day 2 |
| JS Capture Detection | Browser-native screen share | High in Chrome | Yes — Day 2 |
| Dynamic Watermark | Screen recording deterrence + tracing | High deterrent | Yes — Day 3 |
| Right-click / Shortcut Blocking | Casual non-technical users | Low–Medium | Yes — Day 3 |
| Signed URLs + Session Binding | Link sharing, URL scraping | Very High | Yes — Day 2 |
| Key Rate Limiting + Audit Log | Automated key harvesting | High | Yes — Day 4 |
| Full DRM (Widevine L1) | OS-level capture (Netflix-grade) | Maximum | Deferred — Phase 2 |
This deserves its own section because it's often misunderstood. Here's what we actually do, in plain English:
We apply invisible CSS properties to the video player that look normal to a human eye on a real screen, but produce a black or distorted frame when captured by screen-recording software. This works because screen capture tools grab the frame buffer differently from how a monitor renders it. Works on roughly 60% of common capture tools on Windows and Mac Chrome.
Modern browsers expose an API that fires an event when the tab is being captured. We listen for this event — the moment a student starts sharing their screen in the same browser, we automatically pause the video and show a warning: "Screen recording detected — playback paused." Does not catch external apps like OBS capturing the desktop.
Right-click menu is disabled on the video player. Keyboard shortcuts that could save or inspect the page (Ctrl+S, Ctrl+U, F12) are blocked within the playback page. The video element cannot be dragged away. If DevTools is detected as open, the video pauses. These are deterrents for non-technical users — a developer can bypass them, which is why the other layers exist.
When a student switches away from the tab (e.g., to open OBS), we can optionally pause the video and apply a dark overlay — forcing them back to the tab to continue watching. This adds friction without fully blocking determined users.
Edge cases are unusual situations that can break a feature if not planned for. We've identified every major scenario across four categories. Each has a defined handling strategy built into the delivery plan.
Things that can go wrong during an active recording session.
| Scenario | Risk | How We Handle It |
|---|---|---|
| Admin starts recording in an empty room | Medium | Show a warning popup before starting. Recording can still run but admin is informed the room is empty. |
| Admin stops recording within 10 seconds of starting | Low | May produce zero or partial chunks. System will not show this as a "complete" recording — it's marked as incomplete and excluded from the playback list. |
| Server restarts while recording is in progress | High | The recording engine (LiveKit Egress) runs as a separate process and may continue independently. On restart, the cleanup script reconciles any orphaned sessions via the LiveKit API and marks them correctly. |
| Network drops between server and recording engine | High | Chunk upload retries with exponential backoff (wait 2s, then 4s, then 8s). Admin gets an alert if upload fails after 3 retries. |
| Admin tries to start a second recording while one is running | Low | Already blocked in the backend. UI will show a clear message: "A recording is already in progress." |
| Meeting ends while recording is still active | High | Recording is automatically stopped as part of the meeting end flow — a hook is added to the existing meeting end logic. No orphaned recordings. |
| Recording engine crashes mid-session | High | Backend polls the engine status every 30 seconds. If a crash is detected, the recording is marked "incomplete", all chunks captured so far are preserved, and a partial playlist is generated so the available footage is still watchable. |
| Admin clicks Stop but the command times out | Medium | UI shows a loading spinner. After 30 seconds, it polls the status automatically. If the engine is still running, a "Force Stop" button appears. |
Video files are large. These cases protect against unexpected bills and full disks.
| Scenario | Risk | How We Handle It |
|---|---|---|
| Very long meeting (3+ hours) | High | Admin is shown an estimated storage size before starting. 3 hours ≈ 1,500+ chunks ≈ significant cost. Alert triggers if expected size exceeds a set threshold. |
| Firebase Storage quota exceeded mid-recording | High | Upload errors are now detected and trigger an immediate admin alert. Admin Management Panel includes a storage usage dashboard so they can delete old recordings before quota is reached. |
| Server disk fills up during recording | High | System checks available disk space before starting a recording. If less than 2GB is free, recording is blocked. After each chunk is confirmed uploaded, it's deleted from local storage. |
| Old recordings are never deleted | Medium | Admin can set a retention policy (e.g., auto-delete after 30/60/90 days). Can also set a per-recording manual expiry date. Manual delete is always available from the Admin Panel. |
| Large recording causes browser to crash during playback | Medium | HLS streaming loads only small chunks (7 seconds each) into memory at a time — not the entire recording. The hls.js library handles this natively. No browser memory issues expected. |
Edge cases around who can see what, and when access should change.
| Scenario | Risk | How We Handle It |
|---|---|---|
| Student joins the batch after the meeting was recorded | Medium | Under our Hybrid Model, they will see the recording (batch-level automatic access). This is the expected behaviour — confirm with client if exceptions are needed. |
| Student is removed from batch after recording | Medium | Access is revoked immediately — the access check runs live on every request, so no cached permissions exist. Signed URLs also expire in 90 minutes. |
| Student shares their playback link externally | High | Signed URLs expire in 90 minutes. Even if shared, the link is dead quickly. The recipient also has no authenticated session to fetch the AES decryption key — so even a live link is useless without login. |
| Many students streaming the same recording simultaneously | Medium | HLS + Firebase Storage handles concurrent streaming natively via CDN. No special handling needed. Note: ensure Firebase project is on Blaze plan to avoid egress limits. |
| Student seeks to a part of the video that hasn't uploaded yet | Low | The HLS playlist only references chunks that have been fully uploaded and confirmed. The player won't stall on missing chunks — it simply won't show future timestamps until they're ready. |
| Playback on mobile devices | Medium | HLS (.m3u8) is natively supported on iOS Safari. Android Chrome requires the hls.js library, which is already planned. Both platforms will be tested before release. |
| Meeting has no batch assigned | Low | Automatic batch access doesn't apply. Falls back to admin-only access. Admin can still manually grant specific users. |
Especially important for a medical education platform with sensitive content.
| Scenario | Risk | How We Handle It |
|---|---|---|
| Participants are not told they're being recorded | High — Legal | A consent banner immediately appears for all participants when recording starts. A persistent red dot + "Recording in progress" label stays visible for the entire session. This is a baseline legal/compliance requirement. |
| A participant objects to being recorded | Medium | The system cannot stop recording for one person only — LiveKit Egress composites all participants together. The participant's only option is to leave the session. This limitation is clearly communicated in the consent banner. |
| Recordings contain sensitive medical discussions | High | All playback URLs are authenticated — no public access ever. AES-128 encryption means raw files are unplayable. Access audit log records every viewing event. Consider data retention policy for HIPAA/compliance. |
| Admin account is compromised | High | Signed URL expiry (90 min) limits damage window. Audit logs capture all access by IP. Admin accounts should have 2FA enabled (separate security recommendation — not in this spec's scope). |
With a large team, we're compressing what would be a 2–3 week solo developer build into 5 working days by running phases in parallel. Here's what gets done each day:
Add Start/Stop recording buttons to the admin meeting room view. Wire them to the existing backend API (already built). Implement the live recording indicator (red blinking dot + timer) visible to all participants. Add the consent banner that pops up for everyone when recording starts. Add the auto-stop hook so recording ends when the meeting ends. Basic error messages if the recording engine is unavailable.
Build the Hybrid Access Model backend: batch-level auto-access, manual grant/revoke, expiry dates, access check logic. Implement AES-128 chunk encryption — configure LiveKit Egress to encrypt output, build the authenticated key delivery endpoint. Replace all permanent Firebase Storage URLs with signed URLs (90-min expiry). Add screen capture detection + CSS disruption layer.
Build the recording playback page with HLS video player (hls.js for cross-browser, native for iOS). Add play/pause, seek bar, timestamp, fullscreen controls. Implement the animated dynamic watermark (student name + masked email, slowly moving). Build the Admin Recordings Panel: list of all recordings per meeting, delete, grant/revoke access, set expiry, download button. Right-click and keyboard blocking on the player page.
Implement retry logic for chunk upload failures with exponential backoff. Add crash detection for the recording engine with auto-mark-incomplete. Build disk space guard (blocks recording if server disk < 2GB free). Add local chunk cleanup after confirmed upload. Build storage usage dashboard in admin panel. Implement access audit log (who watched, when, IP). Rate limit the AES key delivery endpoint (max 1 request per 30 seconds per user).
Full end-to-end test: start recording → join as student → stop → play back across Chrome, Firefox, iOS Safari, Android Chrome. Access control security test: every role attempts every action, verify 403 on all unauthorized attempts. Signed URL expiry test. Egress crash simulation and recovery test. LiveKit Egress deployment configuration on the production server (if not already running). Final smoke test on production environment.
Admin can start, stop, and manage recordings from within the live meeting room. Auto-stop when meeting ends. Consent banner for all participants.
Entire batch automatically gets access. Admin can override per-person. Expiry dates per recording. 8-step access check runs on every request.
AES-128 encryption, screen capture blocking, dynamic watermark, and 90-minute signed URLs. Medical content protected at every level.
Smooth HLS video player works on Chrome, Firefox, iOS Safari, and Android Chrome. Seek, fullscreen, mobile-responsive layout.
List all recordings, grant/revoke access per user, set expiry, delete recordings, view storage usage, download (admin only).
Every recording view is logged with user ID, timestamp, and IP address. AES key requests are rate-limited and logged. Full accountability.