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#

FamilyAppsPromptStable after grant?
CalendarsCalendar"bite-helper would like access to your calendars"yes (helper path is stable)
RemindersReminderssame, for remindersyes
ContactsContacts"…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 use
  • bite doctor --probe additionally 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 doctor also runs its one-shot Mail probe (bite-crawl --probe-mail) — a real Apple Event that can raise the Mail automation prompt once. It distinguishes authorized / no_accounts / denied_or_empty: a zero-account Mail is a configuration state, not a TCC denial, and denied_or_empty also covers Mail simply not running — launch Mail before permission-chasing.
  • bite doctor --fix opens the matching System Settings pane for anything denied.
  • bite doctor --json prints 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 a fix — e.g. open 'x-apple.systempreferences:com.apple.preference.security?Privacy_Reminders'. Toggle the app in the list; the next call just works. Or run bite 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:

  1. The helper lives at a fixed path (~/Library/Application Support/bite/bin/bite-helper) — refreshes via bite install-helper update it in place, preserving grants.
  2. 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.