Repository navigation
Media Plane
RoomGoblin separates uploaded video delivery from classroom control traffic.
| Port | Responsibility |
|---|---|
3000 |
Controller, REST API, scheduler, display WebSockets, telemetry, media-session commands |
3020 |
Authorized uploaded-media GET/HEAD, HTTP byte ranges, MP4/audio payloads |
This prevents a large or slow MP4 transfer from blocking Clear, Stop, navigation, telemetry, or other classroom commands.
The media listener is not public. Each request is authorized through the normal RoomGoblin control plane before the media process reads the file.
Physical displays use short-lived signed asset tokens. Authenticated controller requests can use the existing RoomGoblin browser session for the internal authorization check.
Because the browser page runs on port 3000 while video bytes come from port 3020, authorized media responses use Cross-Origin-Resource-Policy: cross-origin. This allows the already-authorized browser player to consume the separate-port response; it does not bypass authentication.
Pending media authorization now cancels when the client disconnects and has a three-second absolute timeout even if the upstream sends interim responses. The probe copies only the resource path/query into its fixed loopback URL, so client URL credentials cannot become an unintended Basic Authorization header. Existing signed URLs, session cookies, 401/403 denials, and fail-closed 503 transport errors remain supported. Empty-file range requests return 416 rather than an invalid 206 response; a normal empty-file request still returns 200.
Eight isolated HTTP regression tests passed locally on Linux/Node 22.16.0 on 2026-09-19. They use the actual media process with an authorization fixture, not the full Hub or physical receivers. The earlier live acceptance below does not cover this unreleased hardening. Required CI and controlled receiver testing remain necessary. Uploads now use resumable, owner-isolated sessions with 45 MiB requests, a 5 GiB default total-file limit, chunk and final SHA-256 verification, content-signature inspection, and 24-hour cleanup of abandoned temporary data. Keep Cloudflare's per-request limit at 100 MB. Allow roughly twice the largest expected upload as free persistent storage during final assembly.
New resumable session directories use 0750; metadata, chunks, assembly temporaries and completed media use owner-write/group-read 0640, regardless of umask 0077. Temporary descriptors receive this mode before payload writes, and only verified files are atomically published. Group access assumes the documented production GID and parent-directory permissions; existing sessions are not migrated.
Run node --test test/media-plane-lifecycle.test.js from the repository for the
focused suite. See docs/MEDIA-PLANE.md for coverage, upgrade and rollback gates.
This Git-tracked page is published only when the existing Sync Wiki workflow
successfully pushes it; a prepared PR is not proof of Wiki publication.
RoomGoblin keeps the receiver's MP4 as a persistent media session. The operator can change playback without reloading the file:
- Play / Pause
- Restart / Stop
- Seek / scrub
- Volume / mute
- Playback rate
- Clip start/end boundaries
- Receiver-native loop
- Live session telemetry
Commanded video starts in an autoplay-safe muted state and applies the requested audio state after playback begins. If the browser blocks audible autoplay, RoomGoblin continues playback muted rather than leaving the player stopped.
Physical displays are the playback authority.
- Today preview tiles remain live for receiver state but do not decode the active MP4.
- A video preview shows a lightweight Video active on physical display state.
- Display Studio / Media Library video cards use lightweight VIDEO placeholders instead of loading entire MP4 files as thumbnails.
Alpha.84 passed live production testing:
- control-plane health was ready with database and scheduler checks passing;
- media-plane health was green;
- physical MP4 playback started successfully;
- play, pause, volume, seek, restart/stop and playback-rate controls worked;
- the controller remained responsive during playback;
- sustained MP4 traffic/backpressure appeared on port 3020;
- normal control connections remained responsive on port 3000;
- the earlier
401 Unauthorizedand ChromiumERR_BLOCKED_BY_RESPONSE.NotSameSitemedia failures were no longer present.
A large send queue on port 3020 during video playback is expected when the client/network is consuming a large stream. It is a concern only if port 3000 becomes sluggish, media health fails, or playback/session telemetry stops.
On the RoomGoblin host:
curl -fsS http://127.0.0.1:3000/health
curl -fsS http://127.0.0.1:3020/health
ss -tnp | grep -E ':(3000|3020)\b'During active video, the MP4 connection should be on :3020; controller/API/WebSocket connections should remain on :3000.
Do not move MP4 delivery back to port 3000 to work around playback errors. Check:
- signed/session authorization;
- port 3020 health/listener;
- browser CORP response policy;
- receiver media URL rewrite;
- autoplay/session telemetry;
- HTTP Range responses.
See the canonical technical document: docs/MEDIA-PLANE.md.
-
ESPHome devices — native enrollment, security boundaries, supported controls, and recovery behavior.
-
ESPHome discovery — automatic mDNS discovery and adoption of already-flashed devices.