docs · start
Permissions (TCC)#
bite uses Apple's own frameworks, so macOS permission prompts are part of first use. There is no way (and no legitimate way) around them; bite's job is to make them obvious, one-time, and recoverable.
The four permission families#
| Family | Apps | Prompt | Stable after grant? |
|---|---|---|---|
| Calendars | Calendar | "bite-helper would like access to your calendars" | yes (helper path is stable) |
| Reminders | Reminders | same, for reminders | yes |
| Contacts | Contacts | "…full access to contacts" | yes |
| Apple Events (per app) | Mail, Notes, Messages | "'Terminal' wants to control Mail" — attributed to the parent app of the process chain (your terminal or the agent CLI) | yes once granted to that parent |
Messages history additionally reads ~/Library/Messages/chat.db, which needs Full Disk Access for the terminal/agent app. Without FDA, messages_history degrades to active-chats data; send/list always work.
Doctor: status checks are prompt-free#
bite doctor's status checks read TCC status only (no prompts):
✓ calendar a system prompt appears on first use
✗ reminders denied in System Settings
○ mail will prompt on first usebite doctor --probeadditionally live-tests the Apple Events grants for Mail/Notes/Messages — this does trigger the prompts if pending.- Once the crawl worker is installed, plain
bite doctoralso runs its one-shot Mail probe (bite-crawl --probe-mail) — a real Apple Event that can raise the Mail automation prompt once. It distinguishesauthorized/no_accounts/denied_or_empty: a zero-account Mail is a configuration state, not a TCC denial, anddenied_or_emptyalso covers Mail simply not running — launch Mail before permission-chasing. bite doctor --fixopens the matching System Settings pane for anything denied.bite doctor --jsonprints the same report as one compact JSON object (helper, per-app raw states, storage, clients) for scripts and CI; the exit code (0 clean / 2 problems) is identical to the human form.
Recovery flows#
- Prompt pending (
not_determined): the prompt appears on the first real call in an interactive session. Approve once; done forever. - Denied (
denied): the tool error contains afix— e.g.open 'x-apple.systempreferences:com.apple.preference.security?Privacy_Reminders'. Toggle the app in the list; the next call just works. Or runbite doctor --fix. - Silent denial in non-interactive shells (CI, agents without a GUI session): ScriptingBridge calls fail quietly or return empty results. bite maps this to
permission_denied/app_missing— run the same call from a real terminal once to trigger the prompt.
Why prompts may repeat after upgrades#
TCC attributes Apple Events permission to the requesting app chain (your terminal or agent CLI), not to bite itself, and attributes helper-level grants to the helper binary at its install path. Two rules keep grants stable:
- The helper lives at a fixed path (
~/Library/Application Support/bite/bin/bite-helper) — refreshes viabite install-helperupdate it in place, preserving grants. - If you switch terminals/agent CLIs, the first Apple Events call from the new parent prompts once.
codesign ad-hoc signature is applied on every install. bite ships source-only (compilation happens on the user's machine), so Gatekeeper never stands between users and the helper; a notarized prebuilt channel is a considered Phase 2 addition.
macOS 27+: Apple Events go through OSA#
From macOS 27, raw (non-OSA) Apple Event sends return silent empty replies. bite sends through OSA everywhere — the helper via ScriptingBridge, the Mail crawl worker via NSAppleScript — so prompts and grants behave exactly as documented above.
One caveat: bite-crawl is a separate binary with its own TCC identity. Mail may be granted to the helper yet denied for the crawler — and that denial is silent: the crawl sits in waiting_mail (60 s heartbeats, 24 h cap) and then reports failed; it never returns a permission_denied tool error. If indexing keeps failing while live Mail tools work, run bite doctor — the crawler Mail automation line distinguishes denied_or_empty from no_accounts — and approve the Mail automation prompt once from a real terminal.