docs · reference
The Entity Index#
bite answers searches from a local index, not from Apple Events. Mail search over a large mailbox through Apple Events is minutes-slow; the same search against the index is milliseconds. The index lives in its own LanceDB database in bite's data dir (mode 0700) and holds Mail plus mirrors of Calendar, Reminders and Contacts rows — the raw material for the unified search tool and the bulk mail operations.
On the 124k-message benchmark mailbox (reproduce):
| Operation | bite | Typical per-message AppleScript MCPs |
|---|---|---|
| Index/ingest 100k messages | 7.1 s one-time | n/a (never completes) |
| Full-text search | 8–10 ms | 120 s+ timeout at 100 messages |
| Exact count (66k unread) | 8.9 ms | times out |
| Bulk mark-unread-read | a handful of Apple Events total | 1,834+ events |
How Mail gets in: the crawl#
A detached Swift worker (bite-crawl) owns Mail Apple Events for indexing and bulk operations — the Rust control plane itself never sends Mail Apple Events. The worker walks Mail and stages what it finds as NDJSON batch files in batches/; the control plane ingests them into LanceDB (merge-insert by (app, account, id), so re-reads are free and batches are deleted on success).
The schedule:
- First crawl — the newest 30 days first, then 10-day backfill batches out to a 365-day horizon. Each mailbox×window walk covers at most 20,000 messages.
- Later crawls — when the predecessor completed within 7 days, a delta: one window from the previous completion watermark (minus a 2-day overlap) to now. A stale, failed or cancelled predecessor automatically falls back to the full schedule — a partial crawl can never advance the delta baseline.
- Auto-refresh — when the index is older than
auto_refresh_hours(default 24), the next index touch respawns a crawl. no_body— rebuilds can store mail metadata only (index_rebuildwithno_body, CLIbite index rebuild --no-body): message bodies never enter the local store. The data-minimization counterpart to the purge below.
bite index status shows rows per app, crawl state, pending batches and a hint whenever action would help; index_rebuild respawns the crawl now (full: true replays the whole year).
Search#
search {query, app?, limit?} is full-text across everything indexed — Mail, Calendar, Reminders, Contacts, Notes — BM25-ranked, milliseconds, no Apple Events on the query path. Filter-only queries (no text) come back newest-first, matching the live walk. mail_messages_search is served from the same index once it has Mail rows, with a live Apple Events fallback while the index is empty (deferred feedback during a crawl unless force_live).
Bulk operations#
mail_bulk_mark, mail_bulk_move and mail_bulk_delete run over a whole mailbox or a filtered selection (unread only, older than N days — unfiltered whole-mailbox deletes are refused). Each is one whose-clause script per operation plus verification counts, executed by the crawl worker: a handful of Apple Events total, still O(1) versus the per-message 1,834+. Every bulk call previews first (confirm_required: true with the selection spelled out) and needs confirm: true to execute; Mail moves deleted messages to Trash, so a bulk delete is recoverable.
Purging: scoped, and durable#
index_forget {app?, container?, confirm?} (CLI bite index forget --app <name>, --yes to execute) drops rows from the LOCAL index only — Apple data is never touched — scoped by exact app and/or container (a mailbox, calendar, list, folder or chat). Without confirm it returns a would_delete count; with it, the rows go and the full-text fragments compact immediately, so purged bodies stop answering queries.
The purge is durable: the confirmed scope is recorded in purge-scopes.json (0600) and every later ingest skips rows inside it — delta crawls, full rebuilds and the 365-day backfill all leave the scope out. index_status lists the recorded scopes; ingest reports how many rows it skipped for them. index_wipe (CLI bite index wipe) is the complete reset: rows, watermark and purge records, starting clean.
Both are covered in the privacy statement — the index is a local copy of data you already hold, and it is yours to purge.