Skip to content

Ad-hoc signing on release builds invalidates macOS permissions (Accessibility/Microphone/Screen Recording) after every auto-update #75

Description

@yui0303

Symptom

After updating a release build via update & restart (the Tauri updater), voice typing stops working:

  • In System Settings → Privacy & Security → Accessibility, Parley is still shown as checked, but the app itself detects that it is not authorized.
  • Fully quitting with cmd+Q and relaunching does not help.
  • Pressing fn does nothing at all (the HID event tap cannot be installed because AXIsProcessTrusted() returns false).

Observed in practice after updating to v0.8.7.

Root cause

Release builds use an ad-hoc signature. src-tauri/tauri.conf.json currently has:

"signingIdentity": "-"

Checking the installed /Applications/Parley.app:

$ codesign -dvvv /Applications/Parley.app
Identifier=com.pathors.parley
CDHash=c7c7d57dd2c79218fd0e3db03a84690e825d8629
Signature=adhoc
TeamIdentifier=not set

$ codesign -d --requirements - /Applications/Parley.app
# designated => cdhash H"c7c7d57dd2c79218fd0e3db03a84690e825d8629"

$ spctl -a -vvv /Applications/Parley.app
/Applications/Parley.app: rejected

The key point: with an ad-hoc signature, the Designated Requirement is bound to the cdhash. macOS TCC (Accessibility / Microphone / Screen Recording, etc.) uses the DR to match the app a permission was granted to.

Why it breaks on every update

The cdhash is a hash of that specific build's binary, so it is different on every build. That means after every update & restart swaps in a new binary:

  • New cdhash ≠ the old cdhash recorded by TCC → the DR comparison fails → AXIsProcessTrusted() returns false.
  • The System Settings entry is displayed by bundle id / path, so it still looks checked while actually being void.
  • → fn does nothing and the app reports itself as unauthorized. Microphone and Screen Recording behave the same way (Microphone is just less obvious, because the request is re-prompted automatically).

This is not a bug in the detection logic; it is an inevitable consequence of the signing strategy.

Workaround (users have to redo this after every update)

tccutil reset Accessibility com.pathors.parley

Then relaunch Parley → grant permission again (which records the new cdhash) → cmd+Q and relaunch once more. Microphone / Screen Recording need to be re-granted the same way if you use them.

Permanent fix — move to a stable signature

The DR has to be stable across versions for permissions to survive updates. Two options:

Option A: Developer ID Application + notarization (recommended)

  • The DR becomes bound to the Team ID + bundle identifier (anchor apple generic and identifier "com.pathors.parley" and certificate leaf[subject.OU] = <TeamID>), which is stable across versions → permissions no longer break on update.
  • Bonus: Gatekeeper is clean (spctl accepted), and a first-time download from the website no longer triggers the "cannot verify developer" warning.
  • Cost: Apple Developer Program ($99/year). Secrets needed in CI: APPLE_CERTIFICATE (.p12 base64), APPLE_CERTIFICATE_PASSWORD, APPLE_SIGNING_IDENTITY, APPLE_ID, APPLE_PASSWORD (app-specific), APPLE_TEAM_ID. Natively supported by tauri-action.

Option B: a fixed self-signed certificate (free)

  • Sign every release in CI with one fixed self-signed code-signing certificate. The DR then binds to that certificate (identifier "…" and certificate leaf = H"<cert hash>"), and as long as the same certificate is used every time it is stable across updates → permissions are preserved.
  • This is exactly how dev builds in this project keep their permissions across rebuilds.
  • Limitation: no notarization, so a first-time download from the website is still blocked by Gatekeeper (right-click → Open). But that is no different from the current ad-hoc situation (spctl already rejects it), so nothing gets worse — and in exchange permissions survive updates.
  • CI secret: the certificate .p12 (base64) + password.

Recommendation

Long term, go with Option A (which fixes Gatekeeper too). If we don't want to pay / set up notarization yet, shipping Option B is enough to stop the bleeding: it makes the first-install experience no worse than today, while making permissions survive updates.

Acceptance criteria

  • The Parley.app produced by a release is no longer Signature=adhoc; the designated requirement from codesign -d --requirements - is no longer cdhash H"…".
  • After updating from vN to vN+1 through the updater, previously granted Accessibility permission is still in effect (AXIsProcessTrusted() returns true, fn works) with no re-authorization needed.
  • (Option A) spctl -a -vvv Parley.app returns accepted.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions