Skip to content

Re-enable the event tap when the system disables it; verify installer checksum - #17

Open
vipergsm wants to merge 2 commits into
digimata:masterfrom
vipergsm:fix/event-tap-and-installer-checksum
Open

Re-enable the event tap when the system disables it; verify installer checksum#17
vipergsm wants to merge 2 commits into
digimata:masterfrom
vipergsm:fix/event-tap-and-installer-checksum

Conversation

@vipergsm

Copy link
Copy Markdown

Two independent fixes found while reading the source before installing parrot on a Mac mini. Happy to split them into separate PRs if you'd prefer.

Built and running locally with both applied (swift build -c release, Swift 6.3, macOS 26.5, M-series).

1. The event tap is never re-enabled

macOS disables an event tap on .tapDisabledByTimeout or .tapDisabledByUserInput. The callback treats both as no-ops:

// System disabled our tap; we'll need to re-enable. For now just no-op
// and let the user restart parrot.

The comment is honest about it, but the failure is silent and hard to attribute: parrot keeps running, the menu bar icon stays put, the menu still reads "idle · hold fn to dictate" — and fn does nothing. For something designed to sit in the menu bar for days, the first symptom is usually "dictation stopped working at some point and I don't know when".

This calls CGEvent.tapEnable(tap:enable:true) from the main run loop (the one that owns the tap) and writes a line to stderr so the event is at least visible.

2. The tap subscribes to every keystroke and uses none of them

The mask registers flagsChanged | keyDown | keyUp, but handle() discards everything else on its first line:

guard type == .flagsChanged else { return }

So every keystroke system-wide causes an event.copy() and a main-queue dispatch that does nothing. Three costs: needless main-thread work, a higher chance of triggering exactly the timeout above, and typed content — including password fields — passing through a process that holds Accessibility permission. It's .listenOnly and entirely local, so nothing leaves the machine; it's just avoidable surface area for a background daemon.

keyDown/keyUp are still subscribed under --debug-hotkey, where they're genuinely used.

3. install.sh publishes a SHA256 it never checks

release.yml builds and uploads parrot-macos-arm64.tar.gz.sha256. install.sh never fetches it. The script runs as curl | sh, downloads an unsigned binary, strips its quarantine attribute and moves it into /usr/local/bin — with the checksum sitting unused two lines away. Four lines to close.


Separately, and not something for this PR: it might be worth a README note that under parrot install --launch-at-login the daemon needs its own Accessibility and Microphone grants, distinct from the ones granted to the terminal — and that because the binary is unsigned, TCC keys those grants to the binary hash, so every update invalidates them. It presents as "I updated and it stopped working, but doctor says everything is fine", since doctor run from a terminal is checking the terminal's grants, not the daemon's.

vipergsm added 2 commits July 31, 2026 07:23
macOS disables an event tap on .tapDisabledByTimeout or
.tapDisabledByUserInput. The callback treated both as no-ops, so parrot
kept running with a dead tap: the menu bar icon stayed put, the menu still
read "idle - hold fn to dictate", and fn silently did nothing until the
user noticed and restarted. For a daemon meant to sit in the menu bar for
days that is a hard failure mode to diagnose.

Re-enable the tap from the main run loop (the one that owns it) and log a
line to stderr so the event is visible.

Also narrow the mask. Only .flagsChanged is ever acted on -- handle()
discards everything else on its first line -- but the tap subscribed to
keyDown and keyUp as well, so every keystroke system-wide caused an
event.copy() and a main-queue dispatch for nothing. Beyond the wasted
work, that raises the odds of the very timeout above, and needlessly
routes typed content (including password fields) through an
Accessibility-privileged process. keyDown/keyUp are still subscribed
under --debug-hotkey, where they are actually used.
The release workflow already builds and uploads
parrot-macos-arm64.tar.gz.sha256, but install.sh never fetched it. The
script is invoked as `curl | sh`, downloads an unsigned binary, strips
its quarantine attribute and moves it into /usr/local/bin -- with the
checksum sitting unused two lines away. Fetch and verify it.
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