Skip to content

Windows: driver status banner, OTA never reboots unprompted, no UAC prompt at plug time - #90

Merged
emir-hasanbegovic merged 3 commits into
mainfrom
feat/driver-status-banner
Sep 3, 2026
Merged

Windows: driver status banner, OTA never reboots unprompted, no UAC prompt at plug time#90
emir-hasanbegovic merged 3 commits into
mainfrom
feat/driver-status-banner

Conversation

@emir-hasanbegovic

@emir-hasanbegovic emir-hasanbegovic commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Three things on this branch:

  1. Windows dashboard driver banner: for ViGEmBus and HIDMaestro, whether each is installed, the installed version against the version this build bundles, and whether ViGEmBus is waiting on a restart. Green strip when current; amber/red with the fix as the action.
  2. OTA upgrade never reboots unprompted: the in-app updater ran the installer /VERYSILENT without /NORESTART; a bundled ViGEmBus upgrade over an older driver can exit 3010, which a very-silent Inno run answers by rebooting the PC. Fixed, with driver failures logged instead of raised as modals under /OTA.
  3. No UAC prompt when a HIDMaestro controller connects: setup registers satellite-hm-helper.exe as the LocalSystem service SatelliteHmBroker ("Satellite Controller Broker"). Satellite reaches it over \\.\pipe\satellite-hm-broker instead of spawning an elevated helper, so the first plug of a session no longer needs someone at the PC. The runas path remains as the fallback.

Broker service design

  • Demand-start; started by the SCM named-pipe trigger (sc triggerinfo start/namedpipe/...) or by satellite itself (setup's SDDL grants interactive users SERVICE_START); exits after five idle minutes.
  • Pipe DACL: SYSTEM + Administrators full, Interactive logons read/write (no network logon can reach it). Each connection is admitted only if the client PID is the installed satellite.exe beside the helper (QueryFullProcessImageName, run as SYSTEM) in an interactive session.
  • Satellite accepts the pipe only when GetNamedPipeServerProcessId equals the registered service's PID from QueryServiceStatusEx, so a squatted pipe name is refused, and it falls back to the spawned helper.
  • One client session at a time (the SDK's orphan sweep is machine-global); a second gets busy and falls back.
  • Helper gains service (advapi32 dispatcher, no new NuGet dependency) and broker (console) modes; hello answers "broker":true over the service.
  • Installer: PrepareToInstall stops the service before files are replaced; ssPostInstall creates/reconfigures it (sc create|config, description, sdset, triggerinfo, failure) when the hidmaestro component is selected and /HIDMAESTRO=skip is not passed, otherwise removes it; uninstall removes it before files go.

API (additive, /api/backend/status + /api/server/capabilities)

Per backends[] entry on Windows: driverVersion now filled (ViGEmBus.sys file version; HIDMaestro driver-store INF DriverVer, 1.7.0 embeds 1.4.7.12), plus bundledVersion, versionState (current/outdated/newer/unknown) and restartPending. Pins in src/platform/windows/driver_pins.h, checked against installer.iss by version-consistency.yml.

Tests

  • New pure suite test_driver_inf; test_semver dotted compare; test_backend_registry version-state + JSON fields; test_routes_admin field presence. All local MinGW suites pass; clang-format 22.1.4 clean.
  • Broker, verified unelevated: console broker mode accepts repeated connections, authorizes by client image path (a PowerShell client is refused with client not satellite.exe), and re-creates its pipe instance (a DACL bug there was found and fixed).
  • Broker, needs an elevated shell: scripts/test-broker-service.ps1 registers the service against the built helper, asserts it hosts in session 0, answers on the pipe, restarts from the named-pipe trigger after a stop, and carries the SERVICE_START grant, then removes it. Not run in this session (no elevation available); please run it once before merging.
  • scripts/test-installer-roundtrip.ps1 additionally asserts no service exists under /HIDMAESTRO=skip and after uninstall.

Docs

CHANGELOG 2.0.0 (two paragraphs), README ("Upgrading", broker paragraph, switch table), docs/architecture.md (Elevation story), docs/contract.md (new fields), SECURITY.md (broker boundary in scope), redist/README.md (pin-bump recipe).

Windows dashboard gains a driver banner showing whether ViGEmBus and
HIDMaestro are installed, their installed version against the version this
build bundles, and whether ViGEmBus is waiting on a restart to finish an
upgrade. Green strip when both are current; amber/red with the fix as the
action (pending Satellite update, which carries both drivers, or the
installer for the running version).

/api/backend/status and /api/server/capabilities fill driverVersion on
Windows (ViGEmBus.sys file version; HIDMaestro driver-store INF DriverVer)
and gain additive bundledVersion, versionState and restartPending fields.
Pins live in src/platform/windows/driver_pins.h and version-consistency.yml
keeps installer.iss in step.

Upgrade path fix: the in-app updater ran the installer /VERYSILENT without
/NORESTART, so a bundled ViGEmBus upgrade returning 3010 rebooted the PC
unprompted. It now passes /NORESTART, the installer logs driver failures
instead of raising modals under /OTA, and the banner reports the pending
restart.
The update banner's four buttons overflowed the 440px card: the actions
container had flex-shrink: 0, so once it wrapped onto its own line it kept
its max-content width and its inner flex-wrap never triggered. Let it shrink
and cap it at the row width. The driver banner's action moves onto its own
row, and it no longer duplicates the update banner's Download button when a
pending update already carries both drivers.
…ompt

Creating a HIDMaestro virtual device needs an administrator token; until
now satellite spawned satellite-hm-helper.exe with runas on the first
HIDMaestro plug of each session, one UAC prompt at the moment nobody is at
the PC. Setup now registers the same helper as the LocalSystem service
SatelliteHmBroker (demand-start, named-pipe trigger, SERVICE_START granted
to interactive users, exits after five idle minutes) and satellite talks to
it over \.\pipe\satellite-hm-broker. The broker admits only interactive
logons and only the installed satellite.exe beside it; satellite accepts
the pipe only when its server PID is the registered service's PID. The
runas path stays as the fallback (service absent, /HIDMAESTRO=skip, or a
second concurrent session refused as busy).

Helper gains `service` (advapi32 dispatcher, no new dependency) and
`broker` (console) modes. Installer stops the service before replacing
files, registers or removes it at post-install, and removes it on
uninstall. scripts/test-broker-service.ps1 exercises the SCM-hosted path
from an elevated shell.
@emir-hasanbegovic emir-hasanbegovic changed the title Windows driver status banner + OTA upgrade never reboots unprompted Windows: driver status banner, OTA never reboots unprompted, no UAC prompt at plug time Sep 3, 2026
@emir-hasanbegovic
emir-hasanbegovic merged commit 5082fe4 into main Sep 3, 2026
15 checks passed
@emir-hasanbegovic
emir-hasanbegovic deleted the feat/driver-status-banner branch September 3, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant