summaryrefslogtreecommitdiffstats
path: root/packaging/win/README.md
Commit message (Collapse)AuthorAgeFilesLines
* feat: decode and re-encode video on the GPU where there is oneChristophe Besson7 days1-2/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A 1080p film is decoded by whoever watches it and re-encoded by the node when no browser can decode the source. Both were on the CPU, and on an Atom or Celeron mini-PC neither reaches real time — which is what `transcode_incompatible_video` exists to refuse. This adds the mechanism that makes refusing it unnecessary. Node — `hwaccel.py`: VA-API on Linux, Quick Sync or NVENC on Windows, established by encoding 1080p and reading the file back with ffprobe. Nothing is accepted that does not produce the exact profile and level `stream_init` announces, since the client checks that string before it trusts a byte: an encoder that wrote another level would make the node's own codec string a lie, and ffmpeg takes `-level 4.1` and `-level 41` from h264_qsv without saying which it understood. Three modes per stream — hardware decode and encode, hardware encode alone, libx264 — demoted per source codec, because a GPU that decodes HEVC may have no decoder for MPEG-4 Part 2 and only asking it finds out. A mode that fails is detected on an empty stdout before `stream_init` goes out, so the viewer sees one working stream and never an error. Client — Chromium ships VA-API off on Linux. It is enabled where a render node and a driver are present, then verified through `navigator.mediaCapabilities`: a no moves to the next GL backend on the next launch and an exhausted list drops the switches, so a renamed feature cannot pass for a feature that is on and `--ignore-gpu-blocklist` cannot survive on a machine it did not help. Feature lists now merge rather than overwrite — `appendSwitch` replaces the value, and a second caller would have silently cancelled the mDNS switch aiortc depends on. Packaging — the drivers are weak dependencies on all four formats, so a machine without a GPU installs exactly as before. `dpkg -i` and `rpm -ivh` ignore weak deps; `packaging/README.md` now says so. Windows needs no driver: the bundled ffmpeg already carries h264_qsv and h264_nvenc, and a re-pin that dropped them would cost every low-power Windows node its hardware encoding silently. AMD on Windows (AMF) and macOS (VideoToolbox) are named gaps, not oversights: neither could be tried anywhere in this project, and both re-encode in software as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(packaging): bundle ffmpeg in the Windows installer by defaultChristophe Besson2026-09-051-8/+27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | winget install ffmpeg was considered and rejected as the mechanism: it needs network access and winget/App Installer present at the exact moment setup runs, and its failure mode is silent -- video just does not stream, with nothing pointing back at ffmpeg. Not viable for a non-technical install. MeshBay transcodes browser-incompatible video to H.264 (-c:v libx264, webrtc_server.py) -- a real encode, not remux -- so this needs a genuine GPL ffmpeg build; no LGPL-only build includes an H.264 encoder, since libx264 itself is GPL. packaging/win/fetch-ffmpeg.ps1 (new) Downloads, checksum-verifies and stages ffmpeg for the build. Source: BtbN/FFmpeg-Builds' Windows x86_64 gpl-shared preset -- shared DLLs rather than two independent static binaries, which is what nearly tripled this: the "full" static build many devs already have via winget is ~220 MB *per executable*. Pinned to one dated release tag (immutable once published) and its own sha256, not the "latest" alias BtbN repoints on every auto-build -- verified by hand first (downloaded, hash matched, ran a real encode+probe with libx264) before pinning. ffplay.exe (an SDL2 player, ~17 MB) is dropped; MeshBay never invokes it. Cached after the first build. Runs its own smoke test (encode + probe a real clip) so a broken fetch fails at build time, not for the first user who tries to watch something. packaging/win/LICENSE-ffmpeg.txt (new) GPLv3 notice + where the corresponding source is, required because this redistributes a GPL binary even though it is unmodified and only ever invoked as a subprocess. Ships alongside ffmpeg.exe in the installer. build-node-runtime.ps1 / build-win.ps1 Bundling is now the DEFAULT, replacing the old opt-in -FfmpegDir (which copied from a local directory and left most builds without ffmpeg at all). -SkipFfmpeg opts out for a smaller, streaming-less local-iteration build. Also fixes a real bug the ffmpeg change exposed rather than caused: the final `--help` smoke test did `$help -notmatch "meshbay-node"` against $help captured as a PowerShell ARRAY (one element per line) -- -notmatch on a collection is a FILTER, not a boolean test, and returns the non-matching elements; any non-empty array is truthy in if() regardless of content. Once --help wrapped past one line (it now does, with autostart/service in the verb list) this threw unconditionally. Fixed by joining to one string before matching, and pinned by a new test so a future edit cannot silently reintroduce the collection-vs-scalar trap. Verified: downloaded and hashed the pinned release by hand (matches), ran a real libx264 encode + ffprobe against the extracted build, fetch-ffmpeg.ps1 end to end (161 MB staged), a full build-node-runtime.ps1 run (308 MB node-runtime/) and a full installer build (MeshBay-Setup- 1.0.0.exe, 210.8 MB with ffmpeg bundled). Node suite 850 pass / 25 skip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat: opt-in Windows service mode (boot-time, one elevation) + v1.0.0Christophe Besson2026-09-041-2/+43
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The per-user Startup-folder launcher (W3) only ever runs after this user signs in. A real Windows Service would start earlier, but under LocalSystem/NetworkService -- accounts with no normal profile, so %LOCALAPPDATA%\meshbay\ (config, keystore, data) would not exist for it. Relocating storage to make that work is real surgery, deliberately not done here. Instead: a Scheduled Task, created once with admin rights, that runs AS THIS USER at boot without needing them to sign in first. `schtasks /create ... /ru <user> /rp ""` with no `/it` registers an S4U (Service For User) logon -- no password stored anywhere, and unlike LocalSystem it loads this account's own profile, so config_dir()/ data_dir() need zero changes. The cost: S4U carries no network credential, which the node never needed -- everything it touches is local disk plus outbound internet. Creating the task needs admin (a boot trigger touches system-wide scheduler state, the same reason /sc onlogon needed it); querying/starting/stopping an existing one does not -- Task Scheduler grants the owning user that much itself, which is what lets the Node page's Start/Stop/Restart drive it with no further UAC prompts. meshbay_node/platform.py service_install/_remove/_status/_run/_end -- mirrors autostart_* but for the Scheduled Task; TASK_NAME moved here (was decorative before) meshbay_node/daemon.py new `service install|remove|start|stop|status` verb; restart-daemon and reset now check for the service task too packaging/win/service.ps1 the installer-side equivalent (extraResource); status/run/end never self-elevate -- only install/remove do, exactly matching what Task Scheduler itself requires packaging/win/service-mode.ps1 ONE elevated helper running service.ps1 + firewall.ps1 together, so choosing service mode costs exactly one UAC prompt, not two build/installer.nsh the install-time choice: "run as a background service?" (one elevation, both jobs) vs the existing per-user + separate firewall question. Checked first, unelevated, so re-running setup with everything already configured asks nothing. Uninstall offers the matching one-elevation cleanup, default No. src/main.js winServiceTaskStatus/Run/End, wired into node:installed, node:service-status/-stop/-restart and node:start: when the Scheduled Task exists, drive it; otherwise fall back to the existing per-user spawn/kill path. This is the hard requirement -- Start/Stop/Restart from the Node page must work in either mode. node-page.js / locales a hint explaining why the per-user autostart toggle is absent when service mode is active (info.mode from the backend, no new field to gate on -- it just isn't sent in that case) package.json: 0.1.0 -> 1.0.0. Verified: electron-builder compiles the new NSIS choice logic and ships all three scripts; service.ps1's S4U install fails cleanly (Access denied) when run unelevated, and its status/run/end never touch "runas". Cannot verify the elevated success path myself (no admin in this session) -- that needs a real UAC click. Node suite 843 pass / 25 skip; test_packaging_win.py pins the one-elevation property, the S4U flags, and that main.js actually checks the service task in all three handlers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(packaging): re-running setup no longer re-prompts for firewall accessChristophe Besson2026-09-041-2/+6
| | | | | | | | | | | | | | | | | | | Every run of customInstall showed the "Allow MeshBay through Windows Firewall?" question and, on Yes, a fresh UAC prompt -- an upgrade or repair install would ask again even with all four rules already in place. customInstall now checks first: firewall.ps1 check, unelevated (Get-NetFirewallRule needs no admin, only New/Remove do), exits 0 if every rule already exists. Only a nonzero result reaches the MessageBox and the elevated add. A second run of setup on an already-configured machine now asks nothing. Verified unelevated: check exits 1 and logs which rules are missing on a machine with none of them (the fresh-install case); electron-builder compiles the nsExec::Exec / Pop $0 / ${If} wiring. Node suite 837 pass / 25 skip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat(packaging): cover LAN casting in the Windows firewall step, tooChristophe Besson2026-09-041-12/+29
| | | | | | | | | | | | | | | | | | | | | | | firewall.ps1 only handled WebRTC. The cast HTTP relay (src/cast-relay.js, fixed TCP 19550-19553) and Chromecast/Smart TV mDNS discovery (src/cast-chromecast.js, bonjour-service, UDP 5353) are a separate surface on the client and were still hitting Windows' own "Allow access" dialog. Now four named rules: MeshBay / MeshBay Node stay program-scoped with no port restriction (there is no fixed WebRTC port to name -- the Windows equivalent of the broad 1024-65535/udp range the Linux packaging needs, since netfilter has no program scoping); MeshBay Cast / MeshBay Cast Discovery add the program AND the fixed port, matching packaging/firewall/*/meshbay-cast.xml exactly. test_packaging_win.py cross-checks the port numbers against cast-relay.js's own constants and the Linux firewalld definition, so the three descriptions of one port range can't quietly drift apart. Verified: rebuilt MeshBay-Setup-0.1.0.exe; the deployed firewall.ps1 carries all four rules. Node suite 836 pass / 25 skip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat(packaging): offer one elevated firewall step instead of two dialogsChristophe Besson2026-09-041-8/+15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Installing used to mean clicking through two separate Windows "Allow access" prompts later — one for MeshBay.exe, one for meshbay-node.exe — each confusing on its own and worse before the exe carried a version resource. Adding a firewall rule needs admin, and the installer is deliberately per-user with no elevation, so this can only ever be opt-in. packaging/win/firewall.ps1 (new, shipped as an extraResource at resources\firewall.ps1): idempotent add/remove of the two inbound UDP rules ("MeshBay", "MeshBay Node"), grouped, logged to %TEMP%\meshbay-firewall.log. Locates both executables from its own path, no arguments needed beyond the action. build/installer.nsh: customInstall asks "Allow MeshBay through Windows Firewall now?" and runs firewall.ps1 via NSIS ExecShellWait "runas" — one UAC prompt — only when not ${Silent}; declining or dismissing UAC falls back to Windows' own per-process prompts, unchanged. customUnInstall offers the same in reverse, defaulted to No (a stale rule for a deleted exe is inert, so this should not nag on the way out) and skipped for a silent uninstall. Verified: rebuilt MeshBay-Setup-0.1.0.exe (electron-builder compiles the new LogicLib.nsh / ExecShellWait NSIS successfully); firewall.ps1 run unelevated fails cleanly into its log ("Access is denied") rather than silently doing nothing, confirming the fallback path. Node suite 835 pass / 25 skip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat(packaging): stamp the frozen node exe, document Windows networkingChristophe Besson2026-09-041-0/+41
| | | | | | | | | | | | | | | | | meshbay-node.spec now builds a VSVersionInfo (ProductName "MeshBay Node", version read from the installed package so it tracks pyproject) and passes it to EXE(version=...). The Windows Firewall prompt, Task Manager and the file's Properties then show "MeshBay Node" instead of a bare exe name — the prompt the operator has to answer on first run. packaging/win/README.md gains a Networking section: the first-run firewall prompt (allow Private AND Public — a VM adapter is Public), that a flat LAN needs nothing else, what a routed/multi-subnet LAN additionally needs, the libvirt-NAT caveat, and a pointer to the packaged Linux firewall profiles. test_packaging_win.py covers the version resource. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat(packaging): put the bundled meshbay-node on the per-user PATHChristophe Besson2026-09-041-0/+6
| | | | | | | | | | | | | | | | The installer had no console entry point, so running the CLI meant cd-ing into resources\node-runtime\ every time. installer.nsh now adds that dir to HKCU\Environment\Path on install and removes it on uninstall, via stock WordFunc.nsh (electron-builder's NSIS bundle has no EnVar plugin). It points at the real meshbay-node.exe dir — not a shim — so `where meshbay-node`, findNodeBinary and the W3 autostart launcher all resolve to the same binary. A WM_SETTINGCHANGE broadcast nudges open shells; new ones just work. Rebuilt: electron-builder --win nsis compiles the WordFunc includes fine, MeshBay-Setup-0.1.0.exe (155 MB). test_packaging_win.py covers the add/remove symmetry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat: Windows installer (W4) — one per-user NSIS package, client + nodeChristophe Besson2026-09-041-0/+90
`npm run dist:win` produces MeshBay-Setup-<version>.exe: the Electron client and, beside it under resources/node-runtime/, the frozen meshbay-node daemon (meshbay-common inside it). No hub. Per-user, no elevation — matches the W3 constraint that a logon-triggered scheduled task needs admin. electron-builder / package.json build.win nsis, build/icon.ico, extraResources -> node-runtime/ build.nsis oneClick:false perMachine:false allowElevation:false allowToChangeInstallationDirectory:true dist:win -> packaging/win/build-win.ps1 (mirrors dist -> build-client.sh) packaging/win/ meshbay-node.spec + node-entry.py PyInstaller freeze of meshbay_node.daemon:main. The awkward deps (aiortc, av, aioquic, pydantic_core, uvicorn, watchdog, guessit, blake3, tzdata) are pulled in whole with collect_all — that list is expected to grow when a frozen run raises ModuleNotFoundError. build-node-runtime.ps1 throwaway venv -> pip install -> PyInstaller -> packages/meshbay-client/node-runtime/ (gitignored) build-win.ps1 Node>=22 check, npm ci, Electron bump, sync-ui, node runtime, electron-builder --win nsis bump-electron.mjs the Chromium-CVE "build against latest Electron" policy, out of the PS script (5.1 here-string terminator rules) README.md PyInstaller, not the python-embed zip: the frozen meshbay-node.exe is a genuine relocatable single binary, which is what src/main.js:findNodeBinary spawns (process.resourcesPath/node-runtime/meshbay-node.exe when packaged) and what the W3 autostart launcher points at. The embeddable zip needs pip to make that wrapper and the wrapper bakes in an absolute interpreter path. build/installer.nsh: on uninstall, taskkill meshbay-node.exe and delete the W3 Startup .vbs (it would point wscript at a deleted binary every sign-in). %LOCALAPPDATA%\meshbay\ — node.toml, keystore.enc — is never touched. ffmpeg is not bundled by default (node finds it on PATH); build-win.ps1 -FfmpegDir copies ffmpeg.exe/ffprobe.exe in for a self-contained installer. Verified on the Windows guest: PyInstaller freeze builds first try (node-runtime 147 MB), frozen `meshbay-node status` talks to the live daemon's loopback API; electron-builder --win nsis produces MeshBay-Setup-0.1.0.exe (155 MB), oneClick/perMachine flags applied, node-runtime bundled at the path findNodeBinary expects. test_packaging_win.py (14) pins the config invariants and the NSIS <-> platform.py autostart seam. Node suite 798 pass / 34 skip. Open: Authenticode signing (13.9 — unsigned => SmartScreen), Windows CI (18.3), electron-updater. First clean-machine install + DPAPI + autostart round-trip is a manual check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>