Skip to content

Installer can loop on Accessibility after reinstall with a stale ad-hoc signature grant #93

Description

@Chipagosfinest

Summary

On v0.43.0, reinstalling OpenWispr can leave the Homebrew service and menu-bar process running while dictation never becomes ready. The installer waits for Accessibility: granted, and AppDelegate blocks in its Accessibility polling loop, even though System Settings still shows OpenWispr enabled.

This appears to be a regression from the resolution of #17: both the reinstall path and the app's upgrade path reset Accessibility, but the replacement app is ad-hoc signed and has a different CDHash. TCC can retain/display an allow row associated with the previous binary while AXIsProcessTrusted() remains false for the running binary.

Observed on v0.43.0

  • Installed through the official Homebrew installer
  • brew services reports OpenWispr started and the menu-bar process remains alive
  • Microphone permission is granted
  • System Settings → Privacy & Security → Accessibility shows OpenWispr ON
  • AXIsProcessTrusted() is still false
  • The old TCC grant referenced CDHash 75B88CD0…; the running binary had CDHash 8aced679…
  • Ready. is never logged
  • install.sh waits up to five minutes for Accessibility: granted
  • Relaunching/reinstalling repeats the same permission flow instead of recovering

Why it loops

There are two blocking pieces:

  1. scripts/install.sh runs tccutil reset Accessibility com.human37.open-wispr on every reinstall, then synchronously waits for the app to log Accessibility: granted.
  2. AppDelegate also resets Accessibility when Permissions.didUpgrade() is true, then blocks in:
while !AXIsProcessTrusted() {
    Thread.sleep(forTimeInterval: 0.5)
}

With the stale visible TCC row, users reasonably see the switch already ON and have no indication that macOS expects it to be rebound to the new binary. The app never reaches startListening() or logs Ready., while the installer waits on a state that cannot resolve by itself.

Reproduction

  1. Install and grant Accessibility to one OpenWispr v0.43.0 binary.
  2. Reinstall/replace it through the official installer so the ad-hoc binary CDHash changes.
  3. Observe that Accessibility still appears ON in System Settings.
  4. Start OpenWispr and check AXIsProcessTrusted() / the OpenWispr log.
  5. The service stays alive, trust remains false, and the installer/app wait indefinitely (or until the installer's five-minute timeout).

Expected behavior

  • Routine reinstall/upgrade should not blindly reset a working permission grant.
  • The app should launch into an explicit waiting-for-permission state without blocking the rest of initialization indefinitely.
  • The installer should use a bounded wait and explain the stale-row recovery: toggle OpenWispr OFF, then ON, to bind the grant to the current binary.
  • The installer should clearly report “installed but Accessibility not confirmed” rather than appearing hung or claiming dictation readiness.

Suggested fix

  • Remove the unconditional Accessibility and Microphone resets from the reinstall path.
  • Stop resetting Accessibility automatically from AppDelegate on every detected version change.
  • Replace the blocking while !AXIsProcessTrusted() loop with a non-blocking timer/poller that keeps the menu-bar UI responsive and starts hotkey listening once trust becomes true.
  • Bound the installer's Accessibility wait and print the OFF → ON recovery instruction when the row is visible but stale.
  • Longer term, use a stable signing identity so upgrades do not present as unrelated ad-hoc binaries to TCC.

I have a narrow local patch implementing the first four changes and can open a PR with tests separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions