Watch a video go live
This is the whole publish path as one scene: the admin at the desk, the API, Cloud Storage, the queue, the single ephemeral worker, the readiness gate, the device that plays the result and the bell that tells the admin it happened.
How a video becomes live, and how the worker disappears
A real-world view of the publish path: the admin, the API, storage, the queue, one ephemeral VM and the devices. Every block is clickable; the journey runs in eleven steps.
The admin opens Add video in gstv-admin-web. Four steps: video info, two artwork crops, trailer and feature, then upload.
How to use it
- Step through the journey. The eleven chips run in order: wizard, sign, upload, artwork, save, VM up, boot, transcode, ready, live, shutdown. Play walks them automatically; Back and Next move one at a time.
- Click any object for its plain-language story and its technical facts — ports, key files, what it owns, and how to know it is healthy. Every block carries its documentation link.
- Zoom, fullscreen, export. The controls in the corner zoom in and out, fit the scene,
open it full screen, and download it as SVG or PNG. Keyboard:
+,-,0,F,Esc. - Parallax. Move the pointer across the scene; the floor and the machines shift at different rates to show depth. Reduce-motion settings turn the movement and the fan off.
The eleven steps, in terms of what changes on disk
| # | Step | What becomes true |
|---|---|---|
| 01 | Wizard | The admin has filled video info, two artwork crops, a trailer and a feature. Nothing is written yet. |
| 02 | Sign | staging/{fileId} exists with a seven-day expiresAt; signUpload returns a signed PUT (images) or a resumable session (video). |
| 03 | Upload | Four objects land directly in the buckets, in 8 MiB chunks: thumbnail, thumbnailPreview, trailer, video. The API never carries bytes. |
| 04 | Artwork | completeUpload has already produced <prefix>/<fileId>.webp at quality 82 and deleted the source. The crop rules live in two repos and are changed together. |
| 05 | Save | saveVideo verifies every object, creates videos/{id}, writes flags/shutdownCancel, and pushes queue/transcode/{pushId}. |
| 06 | VM up | The scheduler sees a non-empty queue and ensures exactly one gstv-vps-<env>-worker exists. It creates and deletes; it never stops or starts. |
| 07 | Boot | startup-script.sh asserts metadata, environment identity, buckets, the baked key, the RTDB host and the timers. Any failure deletes the VM. Then gcsfuse mounts input and output. |
| 08 | Transcode | One job at a time — trailer first, then feature. The master playlist.m3u8 is written before the renditions; each finished rung emits a queue/resolutionEvents child. |
| 09 | Ready | evaluateVideoReadiness runs one transaction requiring transcode.status === "completed", a non-empty trailerResolution and thumbnail.thumbnailUrl. Only then is staging removed and isReady set. |
| 10 | Live | notifications holds video.live; the SSE stream toasts the bell and the catalogue's onSnapshot listener picks the document up without a refresh. |
| 11 | Shutdown | Five idle minutes, or the 24-hour cap, and the worker deletes its own instance. The queue child is removed only after success, so a crash is safe to retry. |
Why the worker is drawn as a tower that disappears
The VM is not a service. It has no uptime to protect, no rolling deploy and no health endpoint the platform polls. It is a machine that exists for the duration of a queue, and the queue is the only thing keeping it alive:
- Fixed name per environment —
gstv-vps-<env>-worker, filtered onlabels.env, so the scheduler can always answer "is there a worker?" without listing every instance. - Create and delete only. The scheduler never stops or starts a VM; a stopped VM is a half-state that neither the queue nor the operator can reason about.
- Two exits. Idle for five minutes (
gstv-idle-threshold-ms=300000), or 24 hours of wall-clock time. Both end in self-deletion, not in a shutdown callback the API has to trust.
That is also why the 212-VM incident happened: the create gate counted a queue nobody could drain. A worker is only as ephemeral as its queue is finite.
When a change touches both the worker and the API, deploy the worker image first. A producer running ahead of its consumer is an infinite work signal, not a stuck job — and this scene's sixth step will keep creating machines forever.
Where failures show up in the scene
| Failure | What the scene would show | What the code does |
|---|---|---|
| A chunk upload is interrupted | The upload arc stalls between the admin and the bucket | The resumable session resyncs on 308; nothing is enqueued until saveVideo verifies all four objects. |
| The image conversion fails | Artwork never finishes; save never runs | 422 with the error in staging; re-upload. 409 means the source changed mid-conversion. |
| The worker dies mid-job | The VM blinks out; the queue keeps the child | The child is removed only after success, so the next VM claims it again. |
| Readiness is refused | The gate stays closed; the title card never turns live | The transaction requires all three conditions; markVideoFailed writes video.failed instead. |
Related: Upload to publish has the full contract, and Notifications explains the SSE stream behind the bell.