Uninstalling the Windows host left every audio device it minted behind forever — and the installer script documented that as a decision #145

Merged
enricobuehler merged 1 commits from worktree-win-audio-uninstall-cleanup into main 2026-08-09 19:30:33 +00:00
Owner

Field report: after uninstalling punktfunk, "Punktfunk Speakers", "Punktfunk Microphone" and the per-pad "Wireless Controller" endpoints stay in Windows' Sound settings.

Why nothing removed them

They have no installer payload behind them. The host mints them at runtime as extra devnodes on Valve's streaming-audio drivers, and both providers deliberately re-resolve their devnode across restarts rather than re-minting it — so they persist by design. Nothing in the uninstall walked that surface, and punktfunk-host.iss had written the gap down as a decision:

The host's own minted audio devnodes ("Punktfunk Speakers/Microphone") are likewise left in place - they are plain instances of Steam's streaming drivers, inert without the host.

Persistent across restarts must not mean permanent. Three families leak: minted.rs (Speakers/Microphone), pad_endpoint.rs (per-pad DualSense speakers), audio_probe.rs (devtest leftovers, so a probe run on an operator's box can't outlive the product either).

The fix

A new driver uninstall --audio leg (audio/windows/devnode_cleanup.rs), wired as a third Inno [UninstallRun] entry after the two driver legs and well after service uninstall — a live host re-mints on its next wiring pass, which would make the sweep look like it did nothing. It:

  • restores the default playback device first, if a host that died mid-stream left it parked on our loopback sink. Windows would re-pick on its own once the device vanishes, but by its own ranking rather than giving the operator back the device they had;
  • removes every MEDIA-class devnode carrying one of our three durable Device Parameters owner markers, crash phantoms included;
  • deletes each endpoint's MMDevices record, resolved through the devnode link before the devnode goes — afterwards nothing ties the record to us.

The safety property, checked against real state

Marker-matched, never name-matched. A read-only probe of the box that actually has these devices settles why that distinction is load-bearing:

MARKED | ROOT\MEDIA\0002 | pad=0   | Steam Streaming Speakers
MARKED | ROOT\MEDIA\0003 | role=1  | Steam Streaming Speakers
MARKED | ROOT\MEDIA\0004 | role=2  | Steam Streaming Microphone

All three read "Steam Streaming …" — the Punktfunk name is stamped on the endpoint, not the devnode — so a name-matched sweep would have eaten Steam's own devices. On the same box, Steam's own ROOT\STEAMSTREAMINGSPEAKERS\0000 and ROOT\STEAMSTREAMINGMICROPHONE\0000, VB-Cable at ROOT\MEDIA\0000, and every HDAUDIO/USB device carry no marker and are untouchable here. A ROOT\-enumeration guard means a marker-shaped value on a real sound card can never cost a user their hardware.

One trap that probe caught: PunktfunkPadIndex is 0 for pad slot 0, so the sweep matches on the value name's presence, never on a truthy value — otherwise pad 0 leaks forever.

Deliberately not done

  • The MMDevices half is best-effort and quiet. Those keys are SYSTEM-owned and grant Administrators read only, while [UninstallRun] runs elevated but as a user — so on a stock box the delete is denied. What survives is inert: with the devnode gone the endpoint is NOTPRESENT, which Sound settings only surfaces behind "Show Disconnected Devices", and nothing re-animates it without a devnode to link to. Buying that scrap back would mean an uninstaller seizing ownership of SYSTEM registry keys, which is a worse thing to ship. The device itself — the actual complaint — is gone either way.
  • VB-CABLE from an older bundled install stays, as the existing comment intends: it's a third-party shared component the user may rely on elsewhere.
  • Steam's driver packages are never removed — our instances only ride on them, and Remote Play still needs them.

Verification

Gated on the windows-amd64 runner (C:\pf-verify), shipped tree proven to contain the change:

  • cargo check -p punktfunk-host --all-targets --locked
  • cargo clippy -p punktfunk-host --all-targets --locked -- -D warnings
  • cargo test -p punktfunk-host audio::83/83 ✓ (81 pre-existing + 2 new)
  • cargo fmt --all --check

Built the host there and ran the new leg on that Steam-less box: no punktfunk audio devices to remove, rc 0.

Not yet verified on glass: the removal itself. That needs a box that has the devices; the one that does is a deliberately-deployed substrate setup, and the running host holds the old endpoint ids in a OnceLock, so audio there stays broken until the service restarts and re-mints. Worth doing before this ships.

Field report: after uninstalling punktfunk, "Punktfunk Speakers", "Punktfunk Microphone" and the per-pad "Wireless Controller" endpoints stay in Windows' Sound settings. ## Why nothing removed them They have no installer payload behind them. The host mints them at **runtime** as extra devnodes on Valve's streaming-audio drivers, and both providers deliberately re-resolve their devnode across restarts rather than re-minting it — so they persist by design. Nothing in the uninstall walked that surface, and `punktfunk-host.iss` had written the gap down as a decision: > The host's own minted audio devnodes ("Punktfunk Speakers/Microphone") are likewise left in place - they are plain instances of Steam's streaming drivers, inert without the host. Persistent across restarts must not mean permanent. Three families leak: `minted.rs` (Speakers/Microphone), `pad_endpoint.rs` (per-pad DualSense speakers), `audio_probe.rs` (devtest leftovers, so a probe run on an operator's box can't outlive the product either). ## The fix A new `driver uninstall --audio` leg (`audio/windows/devnode_cleanup.rs`), wired as a third Inno `[UninstallRun]` entry after the two driver legs and well after `service uninstall` — a live host re-mints on its next wiring pass, which would make the sweep look like it did nothing. It: * **restores the default playback device first**, if a host that died mid-stream left it parked on our loopback sink. Windows would re-pick on its own once the device vanishes, but by its own ranking rather than giving the operator back the device they had; * **removes every MEDIA-class devnode** carrying one of our three durable `Device Parameters` owner markers, crash phantoms included; * **deletes each endpoint's MMDevices record**, resolved through the devnode link *before* the devnode goes — afterwards nothing ties the record to us. ## The safety property, checked against real state Marker-matched, never name-matched. A read-only probe of the box that actually has these devices settles why that distinction is load-bearing: ``` MARKED | ROOT\MEDIA\0002 | pad=0 | Steam Streaming Speakers MARKED | ROOT\MEDIA\0003 | role=1 | Steam Streaming Speakers MARKED | ROOT\MEDIA\0004 | role=2 | Steam Streaming Microphone ``` All three read **"Steam Streaming …"** — the Punktfunk name is stamped on the *endpoint*, not the devnode — so a name-matched sweep would have eaten Steam's own devices. On the same box, Steam's own `ROOT\STEAMSTREAMINGSPEAKERS\0000` and `ROOT\STEAMSTREAMINGMICROPHONE\0000`, VB-Cable at `ROOT\MEDIA\0000`, and every HDAUDIO/USB device carry no marker and are untouchable here. A `ROOT\`-enumeration guard means a marker-shaped value on a real sound card can never cost a user their hardware. One trap that probe caught: **`PunktfunkPadIndex` is `0` for pad slot 0**, so the sweep matches on the value name's *presence*, never on a truthy value — otherwise pad 0 leaks forever. ## Deliberately not done * **The MMDevices half is best-effort and quiet.** Those keys are SYSTEM-owned and grant Administrators read only, while `[UninstallRun]` runs elevated but as a *user* — so on a stock box the delete is denied. What survives is inert: with the devnode gone the endpoint is NOTPRESENT, which Sound settings only surfaces behind "Show Disconnected Devices", and nothing re-animates it without a devnode to link to. Buying that scrap back would mean an uninstaller seizing ownership of SYSTEM registry keys, which is a worse thing to ship. The device itself — the actual complaint — is gone either way. * **VB-CABLE from an older bundled install stays**, as the existing comment intends: it's a third-party shared component the user may rely on elsewhere. * Steam's driver packages are never removed — our instances only ride on them, and Remote Play still needs them. ## Verification Gated on the windows-amd64 runner (`C:\pf-verify`), shipped tree proven to contain the change: * `cargo check -p punktfunk-host --all-targets --locked` ✓ * `cargo clippy -p punktfunk-host --all-targets --locked -- -D warnings` ✓ * `cargo test -p punktfunk-host audio::` — **83/83** ✓ (81 pre-existing + 2 new) * `cargo fmt --all --check` ✓ Built the host there and ran the new leg on that Steam-less box: `no punktfunk audio devices to remove`, rc 0. **Not yet verified on glass: the removal itself.** That needs a box that has the devices; the one that does is a deliberately-deployed substrate setup, and the running host holds the old endpoint ids in a `OnceLock`, so audio there stays broken until the service restarts and re-mints. Worth doing before this ships.
enricobuehler added 1 commit 2026-08-09 19:28:58 +00:00
fix(windows): uninstall removes the audio devices the host mints
ci / bun-nix (pull_request) Successful in 25s
ci / web (pull_request) Successful in 1m21s
ci / docs-site (pull_request) Successful in 1m23s
apple / swift (pull_request) Successful in 1m42s
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 2m37s
android / android (pull_request) Successful in 6m12s
ci / rust (pull_request) Successful in 7m37s
d87a8df28d
The field report: uninstalling punktfunk left "Punktfunk Speakers",
"Punktfunk Microphone" and the per-pad "Wireless Controller" endpoints
sitting in Windows' Sound settings forever.

They have no installer payload behind them, which is why nothing in the
uninstall touched them. The host mints them at RUNTIME as extra devnodes
on Valve's streaming-audio drivers, and both providers deliberately
re-resolve their devnode across restarts instead of re-minting it — so
they persist by design. Persistent across restarts must not mean
permanent: the .iss even documented leaving them behind as a decision.

New `driver uninstall --audio` leg (a third Inno [UninstallRun] entry,
after the two driver legs and well after `service uninstall`, since a
live host re-mints on its next wiring pass):

* restores the default playback device first, if a host that died
  mid-stream left it parked on our loopback sink — otherwise Windows
  re-picks by its own ranking rather than giving the operator back the
  device they had;
* removes every MEDIA-class devnode carrying one of our three durable
  owner markers (pad slot, minted role, probe), phantoms included;
* deletes each endpoint's MMDevices record, resolved through the
  devnode link BEFORE the devnode goes.

Marker-matched, never name-matched: our instances are name-identical to
Steam's own, and Steam's devnodes, its drivers, and a VB-CABLE from the
era when we bundled one carry no marker and stay untouched. A ROOT\
enumeration guard means a marker-shaped value on a real sound card can
never cost the user their hardware.

The registry half is best-effort: those keys are SYSTEM-owned and the
uninstaller runs elevated but as a user, so on a stock box the record
survives as an inert NOTPRESENT entry that Sound settings only shows
behind "Show Disconnected Devices". The device itself is gone either
way, and seizing ownership of SYSTEM registry keys from an uninstaller
is a worse thing to ship than that scrap.
enricobuehler merged commit 1317901122 into main 2026-08-09 19:30:33 +00:00
enricobuehler deleted branch worktree-win-audio-uninstall-cleanup 2026-08-09 19:30:36 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#145