Skip to main content
Status: Available (macOS) Your richest personal corpus. iMessage is fetched by imsgcrawl, a local-first crawler that reads Apple Messages (~/Library/Messages/chat.db) under Full Disk Access, decodes the message text, and archives it into a clean local SQLite database at ~/.imsgcrawl/archive.db. DuckBrain only ever reads that archive — it attaches it read-only and maps it to bronze, exactly like the other *crawl sources.

Install imsgcrawl

imsgcrawl isn’t on the Homebrew tap yet — install it from source (needs Go ≥ 1.26.5):
duckbrain init offers to run this for you when you pick iMessage.

Requires Full Disk Access

macOS protects chat.db behind Full Disk Access (TCC). Because imsgcrawl is the tool that reads it, grant FDA to the app that runs imsgcrawl — your terminal (Ghostty, iTerm, Terminal), or bun for the streaming daemon: System Settings → Privacy & Security → Full Disk Access → enable your terminal, then fully quit and reopen it. Without it, imsgcrawl sync can’t read chat.db, so no archive is built and iMessage won’t appear in duckbrain sync status sources. duckbrain doctor warns when chat.db is present but unreadable.

What gets ingested

  • Text lives in attributedBody. ~98% of modern messages store their text in an Apple typedstream (NSAttributedString) blob, not the text column. imsgcrawl decodes it during sync, so the archive’s text column holds resolved message text.
  • Attachments & tapbacks (reactions, edits) yield no text and are dropped.
  • Incremental & idempotent. imsgcrawl keeps its archive up to date; DuckBrain re-ingests it and dedups on a stable per-message id, so nothing piles up. The streaming trigger lane watches the live chat.db and refreshes the archive + re-ingests within about a minute of a new message.

Facet mapping

iMessage is macOS-only. Rows are classification: confidential and never leave the machine.