Interacting with macOS
mercury is macOS-only, and the crates it leans on are where every platform call lives. Keeping them at the edges is what lets the model stay pure and portable.
Permissions
Accessibility is the permission mercury needs. Grant it in System Settings, under Privacy & Security, Accessibility, to the binary that runs: the grant follows the installed path, so ~/.cargo/bin/mercury and a target/debug/mercury are two different grants.
Input Monitoring is not involved. freddie_keyboard creates its tap with CGEventTapLocation::Session and CGEventTapOptions::Default, which is an active tap that can consume and modify events, and that is what Accessibility gates. Input Monitoring gates listen-only taps, which only observe, so a process that remaps anything cannot be running on one.
Without the grant, mercury start still reports a pid. Taking the single-instance lock is the readiness signal and the daemon takes it first thing, before it measures the screens, shows an icon, or grabs the keyboard. The failures land in the log a moment later:
freddie_windows::initreturnsNotTrusted, so the daemon logswindow placement unavailableand carries on. Nothing is cached, and a laterPlaceeffect has no monitor to place within.freddie_keyboard::interceptfails, the daemon logscould not intercept the keyboard, and the worker returns. That drops theStopper, which stops the main run loop, so the process exits rather than sitting there with a dead keyboard.
The failure is silent from the outside, so reset the grant with tccutil reset Accessibility when testing that path.
One more permission is asked for later and only in one place. Copying the front Chrome tab's URL falls back to osascript when nothing has reported one, and that subprocess needs Apple Events. Everything else about Chrome comes from the extension.