From 3ce52774760b222d94d78bc0118e9da2662a809f Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 4 Sep 2026 09:28:14 +0200 Subject: feat: Windows installer (W4) — one per-user NSIS package, client + node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `npm run dist:win` produces MeshBay-Setup-.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 --- docs/PACKAGING-GUIDE.md | 45 +++++++++++++++++++++++ docs/WINDOWS-PORT.md | 98 +++++++++++++++++++++++++++---------------------- 2 files changed, 100 insertions(+), 43 deletions(-) (limited to 'docs') diff --git a/docs/PACKAGING-GUIDE.md b/docs/PACKAGING-GUIDE.md index 0f9997d..5a5c388 100644 --- a/docs/PACKAGING-GUIDE.md +++ b/docs/PACKAGING-GUIDE.md @@ -59,6 +59,51 @@ sudo rpm -e meshbay-client meshbay-hub meshbay-node meshbay-common --- +## Windows + +One installer, **`MeshBay-Setup-.exe`**, carries the client **and** the +node (with `meshbay-common` inside it). There is no Windows hub. + +### Install + +Run the installer. It is **per-user** — no administrator prompt — and lands in +`%LOCALAPPDATA%\Programs\meshbay-client\`. The node daemon ships beside the app +at `resources\node-runtime\meshbay-node.exe`; the client finds it automatically. + +**ffmpeg** is required for video streaming and is *not* in the installer unless +it was built with `-FfmpegDir`. Otherwise install it separately +(`winget install ffmpeg`) so the node finds it on `PATH`. + +### First run + +Open MeshBay and sign in. Use the **Node** page (or a terminal) to provision: + +``` +meshbay-node init +meshbay-node autostart install # run the daemon at every sign-in (no admin) +``` + +Runtime data — `node.toml`, `keystore.enc`, `unlock.key`, `data\` — lives in +`%LOCALAPPDATA%\meshbay\` and **survives uninstall/reinstall**. + +### Uninstall + +*Apps & features → MeshBay → Uninstall*, or the Start-menu *Uninstall MeshBay* +entry. It stops a running daemon and removes the sign-in launcher; it does not +touch `%LOCALAPPDATA%\meshbay\` (the keystore). + +### Build from source + +See [`packaging/win/README.md`](../packaging/win/README.md). On a machine with +Node ≥ 22 and Python ≥ 3.12: + +```powershell +cd packages\meshbay-client +npm run dist:win +``` + +--- + ## Post-install: Node (desktop user) ### 1. Initialize diff --git a/docs/WINDOWS-PORT.md b/docs/WINDOWS-PORT.md index 9b3caee..239dbd1 100644 --- a/docs/WINDOWS-PORT.md +++ b/docs/WINDOWS-PORT.md @@ -1,10 +1,17 @@ # MeshBay — Windows Port -> Status: **audit complete, work not started.** -> Created 2026-09-03 from a full codebase scan. +> Status: **W1–W3 + W5–W8 done; W4 packaging built, not yet run on a clean machine.** +> Created 2026-09-03 from a full codebase scan; progress notes added 2026-09-04. > This document is both the audit results and the implementation plan. > It does not repeat the design decisions already in `desktop-client-v1.md` > (§6.8, §7.5, decisions E8/E12) — read that first. +> +> **What shipped (branch `win-webrtc-stun`):** platform dirs / signals / perms / +> ffmpeg discovery / CLI messages (W1-2-5-6-7-8, commits through `c2620a5`); +> event loop stays Proactor (`5098e6c`); JWT clock-skew leeway (`dad2157`); +> daemon lifecycle via a Startup-folder `.vbs` launcher, **not** Task Scheduler +> (`220e6e7` — `schtasks /create /sc ONLOGON` needs elevation, see §5.3). W4 is +> `packaging/win/` + `package.json` `build.win` — see §5.4. --- @@ -298,37 +305,41 @@ of the Windows port — per-user mode ships first. **Estimated scope:** ~200 lines Python + ~150 lines JS for per-user mode. Service mode is a separate milestone. -### 5.4 Packaging (W4) +### 5.4 Packaging (W4) — **built 2026-09-04** -**Scope:** `package.json` + `packaging/win/` +**Scope:** `package.json` `build.win`/`build.nsis` + `packaging/win/` + one +`src/main.js` line. **One installer**, `MeshBay-Setup-.exe`, per-user, +carrying the **client** and the **node** (with `meshbay-common` inside it). No +hub. -Add to `package.json`: +`packaging/win/README.md` is the build guide. In brief: -```json -"win": { - "target": "nsis", - "icon": "build/icons/icon.ico" -}, -"nsis": { - "oneClick": false, - "perMachine": false, - "allowToChangeInstallationDirectory": true -} -``` - -Add a `packaging/win/` directory with: -- Icon in `.ico` format -- Optional: Authenticode signing script (Phase 13.9) - -The `dist` script gets a platform flag: -`"dist:win": "npm run sync-ui && electron-builder --win nsis"` - -Python node on Windows: **bundled with the Electron app** or installed -separately. The simplest path is embedding Python via `python-embed` (the -official embeddable zip from python.org) and installing meshbay-node into -it. This avoids requiring a system Python install. - -**Estimated scope:** config + build script, no code changes. +| File | | +|---|---| +| `package.json` `build.win` | `target: nsis`, `icon: build/icon.ico`, `extraResources` → `node-runtime/` | +| `package.json` `build.nsis` | `oneClick:false`, `perMachine:false`, `allowElevation:false`, `allowToChangeInstallationDirectory:true` — no admin, ever | +| `packages/meshbay-client/build/icon.ico` | multi-res, generated from `build/icon.png` | +| `packages/meshbay-client/build/installer.nsh` | uninstall: `taskkill meshbay-node.exe` + delete the W3 Startup `.vbs` | +| `packaging/win/meshbay-node.spec` + `node-entry.py` | PyInstaller freeze of `meshbay_node.daemon:main` | +| `packaging/win/build-node-runtime.ps1` | runs PyInstaller → `packages/meshbay-client/node-runtime/` (gitignored) | +| `packaging/win/build-win.ps1` | orchestrator: Node check → `npm ci` → Electron bump → `sync-ui` → node runtime → `electron-builder --win nsis` | +| `packaging/win/bump-electron.mjs` | the Chromium-CVE "build against latest Electron" policy, factored out of the PS script | +| `npm run dist:win` | → `build-win.ps1` (mirrors how `dist` → `build-client.sh`) | + +**PyInstaller, not the python-embed zip.** The frozen `meshbay-node.exe` is a +genuine relocatable single binary — which is what `findNodeBinary` in +`src/main.js` spawns (`process.resourcesPath/node-runtime/meshbay-node.exe`) and +what the W3 autostart launcher points at (`platform._node_exe`). The embeddable +zip needs pip to produce that wrapper, and the wrapper bakes in an **absolute** +interpreter path that breaks the moment the tree is installed elsewhere. + +**ffmpeg** is not bundled by default (the node finds it on `PATH`); pass +`-FfmpegDir` to `build-win.ps1` to copy `ffmpeg.exe`/`ffprobe.exe` in beside the +daemon for a self-contained installer. + +**Still open:** Authenticode signing (Phase 13.9 — unsigned ⇒ SmartScreen), +a Windows CI runner (Phase 18.3), `electron-updater`. First clean-machine +install + `safeStorage`/DPAPI + autostart round-trip is a manual check. ### 5.5 File permissions (W5) @@ -388,24 +399,25 @@ when `sys.platform == "win32"`. ## 6. Execution order ``` -W1 Platform directories ← unblocks everything; testable on Linux -W2 Signal handling ← 10 lines; do it with W1 -W5 File permissions ← trivial guards; do it with W1 -W7 CLI messages ← cosmetic; do it with W1 -W6 ffmpeg discovery ← small; do it with W1 - ──── milestone: daemon runs on Windows ──── -W3 Daemon lifecycle ← largest item; per-user mode first - ──── milestone: daemon starts/stops on Windows ──── -W4 Packaging ← electron-builder config + build script - ──── milestone: installable on Windows ──── +W1 Platform directories ✅ done +W2 Signal handling ✅ done (SIGINT/SIGTERM; CTRL_CLOSE still open — see below) +W5 File permissions ✅ done +W7 CLI messages ✅ done +W6 ffmpeg discovery ✅ done +W8 test suite green on win32 ✅ done (encoding sweep + skipif; 784 pass) + ──── milestone: daemon runs on Windows ✅ (verified end to end) ──── +W3 Daemon lifecycle ✅ done — Startup-folder .vbs (Task Scheduler needs admin) + ──── milestone: daemon starts/stops on Windows ✅ ──── +W4 Packaging ✅ built — one per-user NSIS installer, client + node + ──── milestone: installable on Windows — pending a clean-machine run ──── + CTRL_CLOSE_EVENT handler ← open: window-close / bare taskkill skips _shutdown() Service mode (W3b) ← Phase 2, optional Authenticode signing ← Phase 13.9 CI Windows matrix ← Phase 18.3 ``` -W1 through W7 (minus W3 and W4) can ship as a **single commit** — they are -mechanical, self-contained, and testable on Linux. W3 and W4 are the real -work and can be developed in parallel. +W1–W2–W5–W6–W7–W8 shipped as a run of mechanical commits, testable on Linux. +W3 and W4 were the real work. --- -- cgit v1.2.3