aboutsummaryrefslogtreecommitdiffstats
path: root/packaging/win/README.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-04 09:28:14 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-04 09:28:14 +0200
commit3ce52774760b222d94d78bc0118e9da2662a809f (patch)
tree52a16c7e05080869fbb17b83665b2fb2048b21e2 /packaging/win/README.md
parent220e6e701806213a576ce80fa655cd9cf4a51880 (diff)
downloadmeshbay-3ce52774760b222d94d78bc0118e9da2662a809f.tar.gz
feat: Windows installer (W4) — one per-user NSIS package, client + node
`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>
Diffstat (limited to 'packaging/win/README.md')
-rw-r--r--packaging/win/README.md90
1 files changed, 90 insertions, 0 deletions
diff --git a/packaging/win/README.md b/packaging/win/README.md
new file mode 100644
index 0000000..d02adca
--- /dev/null
+++ b/packaging/win/README.md
@@ -0,0 +1,90 @@
+# Windows packaging (W4)
+
+A single per-user installer — `MeshBay-Setup-<version>.exe` — that lays down the
+Electron **client** and the Python **node** daemon. No hub (server-only, stays
+Linux). `meshbay-common` rides along inside the node runtime.
+
+## What the installer contains
+
+```
+%LOCALAPPDATA%\Programs\meshbay-client\
+├─ MeshBay.exe Electron client
+├─ resources\
+│ ├─ app.asar src/ + ui/ (the interface ships in the package)
+│ └─ node-runtime\
+│ ├─ meshbay-node.exe frozen daemon (PyInstaller onedir)
+│ ├─ _internal\ … its Python + deps (aiortc, av, aioquic, …)
+│ └─ ffmpeg.exe, ffprobe.exe only if built with -FfmpegDir
+└─ Uninstall MeshBay.exe
+```
+
+Runtime data stays where the node already puts it: `%LOCALAPPDATA%\meshbay\`
+(`node.toml`, `keystore.enc`, `unlock.key`, `data\`). The installer never writes
+there and the uninstaller never deletes it — installers place files, not secrets.
+
+## Build
+
+On a Windows machine with **Node ≥ 22**, **Python ≥ 3.12** (`py -3.12`) and
+**git**:
+
+```powershell
+cd packages\meshbay-client
+npm run dist:win
+```
+
+That runs [`build-win.ps1`](build-win.ps1):
+
+| Step | |
+|---|---|
+| 1 | Node ≥ 22 check |
+| 2 | `npm ci` + download Electron's Chromium |
+| 3 | bump Electron to the latest release (Chromium CVE policy; `-NoElectronBump` to skip) |
+| 4 | `npm run sync-ui` — copy the interface from `meshbay-hub/.../static` |
+| 5 | [`build-node-runtime.ps1`](build-node-runtime.ps1) — PyInstaller freeze → `packages/meshbay-client/node-runtime/` |
+| 6 | `electron-builder --win nsis` → `packages/meshbay-client/dist/MeshBay-Setup-<version>.exe` |
+
+### ffmpeg
+
+Not bundled by default — the node resolves `ffmpeg`/`ffprobe` from `PATH` at
+startup, and video streaming needs them. To make the installer self-contained:
+
+```powershell
+npm run dist:win -- -FfmpegDir "C:\path\to\ffmpeg\bin"
+# or: $env:MESHBAY_FFMPEG_DIR = "C:\path\to\ffmpeg\bin"; npm run dist:win
+```
+
+They are copied beside `meshbay-node.exe`, which is on the daemon's search path.
+
+### Iterating
+
+```powershell
+# rebuild only the installer, reuse an existing node-runtime\
+powershell -File ..\..\packaging\win\build-win.ps1 -SkipNodeRuntime
+
+# build just the frozen daemon, into an existing venv, keep it for next time
+powershell -File ..\..\packaging\win\build-node-runtime.ps1 -Python C:\path\python.exe -KeepBuildVenv
+```
+
+## Why PyInstaller and not the python-embed zip
+
+The frozen `meshbay-node.exe` is a genuine relocatable single binary. It is what
+the client spawns (`findNodeBinary` in `src/main.js`) and what the W3 autostart
+launcher points at (`meshbay_node.platform._node_exe`). The python.org
+embeddable zip would need pip to make a `meshbay-node.exe` wrapper, and that
+wrapper bakes in an **absolute** interpreter path — it stops working the moment
+the tree is installed somewhere other than where it was built.
+
+## Dependency surface that needs watching
+
+`meshbay-node.spec` pulls the awkward packages in whole (`collect_all`) because
+they have C/Rust extensions or do dynamic imports: `aiortc`, `av` (bundles
+FFmpeg DLLs), `aioquic`, `pydantic_core`, `uvicorn`, `watchdog`, `guessit`,
+`blake3`, `zstandard`, `msgpack`. If a frozen run raises `ModuleNotFoundError`,
+add the package to `COLLECT_ALL` / `HIDDEN` / `COPY_META` in the spec — that is
+the expected way the list grows.
+
+## Not done
+
+- **Authenticode signing** — Phase 13.9. Unsigned installer triggers SmartScreen.
+- **CI** — no Windows runner builds this yet (Phase 18.3). Build is manual.
+- **Delta updates / auto-update** — `electron-updater` is not wired.