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:
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
Related
Symptom
After updating a release build via update & restart (the Tauri updater), voice typing stops working:
cmd+Qand relaunching does not help.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.jsoncurrently has:Checking the installed
/Applications/Parley.app: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
cdhashis 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:AXIsProcessTrusted()returns false.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)
Then relaunch Parley → grant permission again (which records the new cdhash) →
cmd+Qand 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)
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.spctlaccepted), and a first-time download from the website no longer triggers the "cannot verify developer" warning.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)
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.spctlalready rejects it), so nothing gets worse — and in exchange permissions survive updates.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
Parley.appproduced by a release is no longerSignature=adhoc; the designated requirement fromcodesign -d --requirements -is no longercdhash H"…".AXIsProcessTrusted()returns true, fn works) with no re-authorization needed.spctl -a -vvv Parley.appreturns accepted.Related
src-tauri/src/hotkey.rs(the fn HID tap, gated onAXIsProcessTrusted/CGPreflightListenEventAccess)