ADR-PROD-011 - Native Windows OBS on a physical GPU host is the vision mixer
[redacted: category] — 4 private address. Nothing else was altered. The document is otherwise exactly as it is written in the repository, and the sha256 below is of the original, so what was ingested stays checkable.How to read this page
A Plain and a Clear version of this page have not been written yet. What follows is the document itself.
Precise — the source document
This is the document. Rendered from the repository at the commit above, with nothing rewritten for the web. A gate re-renders it on every deploy and fails the build if a single byte differs.
- Status: Accepted
- Date: 2026-07-12
- Deciders: UNI Production architecture (operator + producer-agent design)
- Supersedes-in-part: ADR-PROD-001 (headless containerized OBS as the vision mixer) — for the render/mixer placement, not the set-once / director / single-feed shape
- Master contract:
docs/STUDIO_SYSTEMS.md(canonical corrected architecture), the P1–P8 remediation plan~/.claude/plans/fully-plan-all-remediation-snappy-sky.md
Context
Three sessions attempted to run the vision mixer as a headless containerized OBS on uni-lab-79740c, a
Linux node with no GPU (Matrox G200 class). Every attempt produced the same failure: OBS's CEF browser
sources — which composite the 2D-canvas overlays (ADR-PROD-005) and the studio browser channels — software-
render to a black frame under Mesa/llvmpipe with no accelerated compositor. --enable-webgl /
--use-gl=egl did not fix it; the missing piece is a real GPU + display server, not a flag. The result: a
public black-frame push to YouTube+Twitch and three days of visible failure.
The set-once, director-driven, single-encode shape of ADR-PROD-001 is not in question — it worked and still works. What was wrong was the placement: assuming a GPU-less headless container could render CEF-composed scenes. It cannot, today.
Meanwhile THINKER (Windows 11, LAN [redacted: private-address], NVIDIA T1000 4 GB + Intel UHD 630) already runs the
studio surface (viewer/), has a real GPU, and produced a working native OBS mixer ~48 h before this ADR
was written. The path of least regret is to keep the mixer where it already renders, and collapse the Linux
node to fan-out relay only.
Decision
The vision mixer is native Windows OBS Studio on the physical GPU render host (THINKER). Scenes are
built once by viewer/studio_stage.cjs (idempotent) and driven by verbs over obs-websocket. The mixer is
minimized to the system tray at boot; the operator sees only the tray traffic-light + the command_center
Chrome window (P3 of the plan).
Container-form OBS is deferred until a headless-GL-with-real-GPU stack is available on a non-ERP Linux
node (candidates: X11 + NVENC + hardware CEF acceleration inside a container, or Wayland/EGL surfaceless
with GBM). Until that stack exists and passes a real overlay-composition gate (viewer/verify_overlays.cjs
against a container-rendered OBS), production stays on THINKER.
Scope note (2026-07-12): this ADR governs the RENDER / MIX / ENCODE host only. It did not, and must not be read to, move the colony source onto THINKER. The colony (Minecraft world + Phoenix/
SP.ProducerFEP brain +body.jsbots) runs rootless on UNI-LAB — "the chip" — always; THINKER captures it over the LAN and never hosts it. Colony placement is governed by ADR-PROD-013.viewer/studio_up.ps1's localjava -jar paper.jar(:178) + local Phoenix (:224) launch is the KNOWN BUG that ADR-013 reverses.
Consequences
Positive:
- Overlays (2D-canvas via CEF) render correctly — this is the whole point.
- NVENC (T1000) becomes available for the encoder — G-ENC largely closes; x264
faster720p30 stays as the documented software floor if NVENC is unavailable. - The Linux node's role clarifies: single-purpose fan-out relay (
uni-bcast-relay→ YT + Twitch tee). - Boot flow is tray-only: no visible
cmd/ PowerShell windows anywhere; command_center Chrome auto-opens after/api/state=200 as the single operator surface.
Negative:
- THINKER is now the encoder SPOF. Mitigated by: (a) a documented "cold-standby OBS install on node2"
runbook step (< 60 min rebuild if THINKER dies mid-show), (b) tray watchdog that auto-restarts
overlay_server / command_center / publisher on crash, (c) an OBS Safe-Mode / crash-recovery dialog
watcher (
Dismiss-OBSDialogsinviewer/systray_watchdog.ps1) that dismisses the modal within 5s. - OBS's
.sentineldirectory (30+ writes a marker file at startup, deletes it on clean shutdown) triggers Safe Mode on the next start if the box lost power / was hard-killed. Mitigated by clearing that directory inviewer/studio_up.ps1before every OBS launch. - Container-form OBS work is not thrown away — it remains scoped as a future ADR when the surrounding stack (headless GL + real GPU passthrough + verified overlay composition) exists.
Gates preserved
- Never claim overlays-up from process existence —
viewer/verify_overlays.cjsexit-0 +overlay_proof.pngscreenshot remains the gate. - Never claim LIVE from process existence — the 3-signal machine gate (OBS
GetStreamStatus.outputActive+ relay/v3/paths/listreaders ≥ 1+bytesReceivedgrowing on node2) is the gate. - Human-typed
CONFIRMon GO LIVE / OFF AIR (G-PA) is unchanged.
What did not change
- The set-once mixer + director model (ADR-PROD-001, spirit).
- Single-encode → copy fan-out (ADR-PROD-008).
- 2D-canvas overlays composited into the program scene as browser sources (ADR-PROD-005).
- Stream keys live only in
/etc/uni/runtime.envon node2. Never git, never held by an agent. - The Linux node continues to be non-ERP (
uni-lab-79740cmesh[redacted: private-address]). L1 (uni-labmesh[redacted: private-address]/ LAN[redacted: private-address]) is the SAME box that hosts the colony: it is BOTH the rootless UNI-OS colony host (the colony runs there rootless underuni— ADR-PROD-013) AND the rootful ERP appliance. Its broadcast / render / encode surface is zero (ADR-PROD-012); the colony (no GPU) legitimately runs there.
sha256 e4221ec10d345da7 — of the original file, so what was ingested stays checkable.