T-HUB/infra/peertube-workers/WINDOWS.md
glass beadian aa5e262f3a Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
2026-10-10 11:56:42 +00:00

12 KiB
Raw Permalink Blame History

Windows native NVENC worker

Use Windows 10/11 x64 with an NVIDIA GPU and a working driver. WSL is the SSH transport only: Node, FFmpeg, the shim, admission, and IPC all run as Windows processes. The initial worker name is worker-laptop-win; root is C:\pt-worker. The canonical instance is https://tube.occupymedia.cc; worker traffic uses http://TAILSCALE_IP:9000 inside encrypted Tailscale. Viewers use PeerTube. No server configuration change is needed: remote VOD is already enabled.

Prepare and enroll

Run the installer from this directory. Python 3.12+ and Windows Tailscale are build/access prerequisites. Python is embedded in the frozen executables, so services do not depend on the developer's Python installation. Pins are in win/dependencies.json; the FFmpeg URL is a dated archive, verified by SHA256. The two executables are built from reviewed Python source using the versions in win/build-requirements.txt. Runner and IPC dependencies use the existing runtime lockfile. Do not substitute a .cmd shim: Node's shell-free spawn rejects it with EINVAL.

# Elevated PowerShell: installs Node MSI silently plus isolated Node 22.
.\install-peertube-worker-windows.ps1 -Phase Prepare -Name WORKERNAME-win
# A filtered SSH/WSL token can prepare everything except the MSI:
.\install-peertube-worker-windows.ps1 -Phase Prepare -UserLevel -Name WORKERNAME-win
.\install-peertube-worker-windows.ps1 -Phase SelfTest

Both H.264 and HEVC must produce a real 5-second 1080p encode and positive NVIDIA encoder utilization. GPU identity, UUID, driver, boot, device index, exit codes, samples, and session statistics are recorded in state\nvenc.json. No proof means no enrollment. The installer keeps admission PAUSED. A failed init, absent telemetry signal, reboot, or driver mismatch closes admission. Session-0 proof runs again when services are installed; enrollment-session success alone does not prove service-context access.

Supply the existing registration token through stdin. Never pass it as a PowerShell/Node argument, environment variable, transcript, or source file. For SSH transport, pipe PostgreSQL's selected token output directly into the Windows Node bin\ipc-nvenc.cjs register process. The version-controlled win/register-from-ssh.py --worker USER@HOST --name NAME implements this using two subprocesses and pipe handles; it fetches runnerRegistrationToken.registrationToken, database peertube, using the PostgreSQL container's existing POSTGRES_USER. No token is printed or retained. Before doing so, run bin\enroll-server.cjs: it requires concurrency 0 and drops all upstream logs. After registration use ipc-nvenc.cjs graceful-shutdown, wait for the listener to exit, and set TOML concurrency to 2. Alternatively use:

# The calling process must give this process the secret on inherited stdin.
.\install-peertube-worker-windows.ps1 -Phase Enroll -RegistrationTokenStdin

The registration secret is never saved. The official runner necessarily saves its separate issued runner credential in profile\Roaming\peertube-runner-nodejs\Config\default\config.toml. NTFS ACLs restrict this tree to the owner, SYSTEM, administrators, and LocalService. Never print that TOML, arbitrary runner logs, database token columns, or HTTP headers. Enrollment pending markers prohibit blind duplicate registrations.

Services and operations

# ServiceFiles prepares concrete WinSW XML/exe files without touching SCM.
.\install-peertube-worker-windows.ps1 -Phase ServiceFiles
# Elevated PowerShell; services initially PAUSED, session-0 proof first.
.\install-peertube-worker-windows.ps1 -Phase Services
C:\pt-worker\pt-worker-win.ps1 status
C:\pt-worker\pt-worker-win.ps1 network-test
C:\pt-worker\pt-worker-win.ps1 start
C:\pt-worker\pt-worker-win.ps1 drain
C:\pt-worker\pt-worker-win.ps1 restart
C:\pt-worker\pt-worker-win.ps1 self-test

WinSW 2.12.0 wraps pt-runner (Node supervisor) and pt-admission (frozen Python). Both use the WinSW 2.12 service-account and stop configuration, running as LocalService with automatic delayed startup, a 30-second service restart delay, and limited rotated logs. The supervisor uses a further 15-second runner restart delay. It retains only safe milestones and observer events; unfiltered upstream logs are discarded because they can contain tokens. A service stop asks the supervisor to drain via the official named-pipe IPC, with an explicit one-hour SCM stop ceiling. For jobs that can exceed this ceiling, use pt-worker-win drain, wait for runner_running=false, then stop services. No admission loss forcibly kills an active encode.

State decisions use the Linux Meter/Admission semantics: AVAILABLE, BUSY, DRAINING, NETWORK_DEGRADED, GPU_UNAVAILABLE, LOW_DISK, THERMAL_THROTTLED, OFFLINE, PAUSED. Status preserves Linux field names (status, disk_free_bytes, registration_state, last_job_duration_seconds, last_job_realtime_factor, util, etc.) and adds state, NVENC capability flags, and fleet aliases. QSV flags remain false; a successful NVENC test is never reported as QSV success. The supervisor requires a fresh admission document (<30 seconds), an explicit unpaused control document, and no GPU-refusal marker. Missing/stale admission always triggers the official runner graceful-shutdown request.

AC policy uses GetSystemPowerStatus: battery or unknown AC drains. Thermal zones are read through root/wmi:MSAcpi_ThermalZoneTemperature when supported; unavailable CPU sensors remain null. NVIDIA temperature remains a real reading. Defaults: 20 GiB disk reserve, 85 C thermal ceiling, 120 s thermal cooldown, 10 Mbps each direction, 1.25x bandwidth recovery margin, three good/two bad samples, 900 s bandwidth expiry, no DERP admission. The existing Tailscale-only server probe (TAILSCALE_IP:9044) uses bounded 8 MiB GET download and PUT upload checks, EWMA, and runs while the runner is drained. GPU failure events remain latched until a later real self-test. Boot/driver changes trigger new proof. powercfg /change standby-timeout-ac 0 is an elevated install step. The laptop must remain on AC; lid-close/Modern Standby behavior still needs owner review.

Adapter contract and tests

The pinned runner requests libx264. The executable accepts only reviewed command shapes and rewrites them before executing FFmpeg: CUDA/NVDEC hardware frames -> scale_cuda -> h264_nvenc, preset p4, preserving bitrate cap/GOP. The original libx264 command is never executed. H.264, SDR HEVC, and VP9 sources have reviewed hardware-decode routes; rotated, HDR, unsupported pixel formats, unknown options, stream codec overrides, extra outputs, and unknown codecs fail closed with exit 78. Audio-only and explicit stream-copy/remux jobs are permitted without video encoding. Static audio-merge images use software image processing and hwupload_cuda; video encoding remains NVENC. AV1 input is refused until a reviewed decode route is tested; AV1 capability is not inferred from FFmpeg's encoder list. A local diagnostic can set PT_NVENC_TARGET=hevc_nvenc; any other target is refused. Never change the PeerTube service target without a reviewed codec contract. HEVC encoding is proven by the gate but the PeerTube default output remains H.264, matching the pinned runner/server contract.

python3 -m unittest discover -s infra/peertube-workers/win/tests -p test_nvenc.py -v

A running encode that fails, or lasts >=3 seconds without a readable positive encoder signal, returns failure and latches GPU_UNAVAILABLE. Very short encodes may finish within NVIDIA's utilization averaging window; their explicit NVENC encoder and current boot/driver proof remain required. For a real E2E test record positive encode telemetry, job UUID, server Pending/Processing/Completed states, input/output codec and resolution, wall time, RTF, and playable result. Local synthetic success is not a PeerTube E2E result.

Failure checks: drain -> runner gracefully exits; start/restart -> reconnect milestone; create state\gpu-unavailable -> admission GPU_UNAVAILABLE and shim exit 78; remove marker, drain, rerun self-test, then start. Do not unload the owner's desktop GPU driver to simulate failure. Test reboot recovery and AC loss only after services and the owner's physical AC/lid policy can be exercised.

Deployment-specific elevation blocker / worker 2

The verified route is SSH into WSL using ~/.ssh/devserver, then native PowerShell/Node through /mnt/c. Interop works. The Windows token is medium integrity with administrators marked deny-only. sc create fails with OpenSCManager error 5; registering a Highest scheduled task returns access denied. Enabling alternate-port Windows sshd needs the same unavailable elevation and would not remove this blocker. No existing recovery task, UAC policy, driver, or server setting was changed to bypass it. The owner must run the prepared Services phase in an elevated Windows PowerShell. An interactive native process deployment can be validated over the working route but is not a boot-persistent service.

Worker 2 changes only name, root (if wanted), GPU device index, and local network access. Reuse this server probe and existing authorized token; give each worker a unique runner name and isolated profile. Do not copy worker 1's credential TOML. Require a fresh real hardware gate on that GPU and service context. Start with concurrency 2; benchmark equivalent real workloads at 1–4 before increasing it. The RTX 3090's stock session ceiling is not a two-job constraint; no driver patch is deployed. Its measured limit is 350 W; an optional 200–250 W cap needs driver support and throughput/thermal measurement before adopting it. No power limit or driver was changed during deployment. Hardware throughput/session/codec claims from research remain estimates until measured on the specific worker.

Authorized E2E fixture

win/e2e-peertube.mjs is an explicit, privileged server test, never an installer autostart step. Run it only for an authorized private upload, with other workers drained if a specific worker must own the job. Supply a synthetic H.264 file as /tmp/pt-worker-e2e-source.mp4 inside the PeerTube container, then run the script there through Node stdin with NODE_DB_LOG=false and LOGGER_LEVEL=error. It requires the inspected PeerTube 7.3.0 model layout. It registers the installed models without the initializer's SQL function/extension mutations, creates one 10-minute admin OAuth session through the installed token model, performs one private upload through the HTTP API, records safe state metadata, checks private HLS/media HTTP responses and full decoding, then revokes/removes that session. It does not change the root password or runner registration token. Credentials remain in process memory and the temporary normal OAuth database row; they never appear in command arguments, report JSON, or source. Upstream ORM logging is disabled before the token is created. The private test video remains available for owner review; delete disposable server/container scratch files afterward.

First upload with the target worker drained to capture Pending, then start the worker while the helper polls. Report JSON goes to /tmp/pt-worker-e2e-result.json inside the container; it contains no credentials. This host's successful private fixture is Windows NVENC worker E2E, video UUID 324566b4-3506-417d-9cca-cc2a3b7a14cc. Jobs 1/2 completed on runner 1. The cold WebVideo encode took 36.359 s with positive 100% NVIDIA encoder samples; its warm HLS encode took 2.594 s. These are observed 5-second-fixture timings, not a concurrency benchmark. The short HLS run sampled 0% in NVIDIA's averaging window; its command was still explicitly NVENC, and the WebVideo job supplies mandatory job-correlated GPU proof. Root OAuth sessions were verified back at 0.