# iMessage (`IMessageSync`) ## Purpose Sync iMessage / SMS conversations and send messages from the web back to contacts via Messages.app. ## Direction Bidirectional. Inbound: read `chat.db` -> push to server. Outbound: poll the legacy `icloud.send_queue` table -> AppleScript-driven send via Messages.app. ## OS surface `~/Library/Messages/chat.db` (SQLite, accessed via GRDB) plus Messages.app via AppleScript for outbound. Requires **Full Disk Access**; module surfaces `.fullDiskAccessRequired` when missing (`@packages/imessage/Sources/IMessageSync/SyncManager.swift:36, 132-133`). Contacts are loaded via Contacts.framework (`@packages/imessage/Sources/IMessageSync/Reader.swift:77`). ## Files - `Reader.swift` (`@packages/imessage/Sources/IMessageSync/Reader.swift`): `class iMessageReader` (line 64) — `connect()`, `getConversations()`, `getMessages(conversationId:since:)`, `loadContacts()`. `getMessages` now invokes `extractTextFromAttributedBody(_:)` (line 371): when chat.db's `text` column is null/empty and `attributedBody` is non-nil, the byte-scan heuristic recovers plain text and substitutes it before sending. Limits of the heuristic — single text run only, no rich formatting — are documented in [known-limitations](../known-limitations.md#imessage-attributedbody-decoder-is-heuristic-not-a-full-typedstream-parser). - `SyncManager.swift` — `final class SyncManager: BaseSyncManager` (line 44); 30s timer (line 93). Owns `sendQueueClient: SendQueueClient` (lines 70-86), `didStartSync` / `willStopSync` (lines 170-176). Handles schema-version migrations via `Self.syncSchemaVersion` (line 47) and chunked payload retries (lines 331-354). - `APIClient.swift` — `syncMessages`, `syncContacts`, `getStats`, `getPendingSends`, `reportSendResult`, `resetSync` (`@packages/imessage/Sources/IMessageSync/APIClient.swift:14-108`). - `Sender.swift` — AppleScript driver `SendService`; uses `AppleScriptEscape` (`@packages/shared/Sources/MacSyncShared/Util/AppleScriptEscape.swift:13`) for all string interpolation. - `SendQueueAdapter.swift` — `IMessageSendTransport` adapting the bespoke legacy send-queue endpoints into the generic `SendQueueTransport` protocol. - `ChunkingStrategy.swift` — adaptive batch sizing for large message + attachment payloads. ## Timing - Read interval: **30s** (`@packages/imessage/Sources/IMessageSync/SyncManager.swift:93`). - Outbound poll interval: **30s** (`@packages/imessage/Sources/IMessageSync/SyncManager.swift:74`). - Inbound batch size: adaptive via `ChunkingStrategy.createAdaptiveChunks` (line 202). ## Server surface - Entity tables: `icloud.contacts`, `icloud.conversations`, `icloud.messages`, `icloud.send_queue` (legacy) (`src/server/src/app/server.ts:38-49`). - Client routes (`src/server/src/surfaces/client/imessage.ts`): - `POST /client/imessage/sync` (line 71) - `POST /client/imessage/contacts` (line 77) - `GET /client/imessage/stats` (line 83) - `GET /client/imessage/send-queue/pending` (line 88) - `POST /client/imessage/send-queue/:id/result` (line 126) - `POST /client/imessage/reset` (line 172) - Web routes (`src/server/src/surfaces/my/messages.ts`): - `GET /my/messages/conversations` (line 11) - `GET /my/messages/conversations/:id/messages` (line 15) - `GET /my/messages/last-sync` (line 21) - Admin enqueue: `POST /admin/send-queue/enqueue` (`src/server/src/surfaces/admin/send-queue.ts:15`). ## Web surface - Tab: `/messages` (`web/src/App.tsx:57`). - API helpers: `web/src/api/messages.ts`. ## Known limitations - `attributedBody` is forwarded raw; macOS 13+ messages with no plaintext appear blank in the dashboard. See [known-limitations](../known-limitations.md#imessage-attributedbody-is-not-parsed). - AppleScript send can stall if Messages.app is unresponsive; manual restart required. - The send queue is on a bespoke schema (extra columns vs the generic `_send_queue`); admin tooling must special-case it. ## Tests (`@packages/imessage/Tests/IMessageSyncTests/`) - `ChunkingTests.swift` — payload sizing/splitting (hermetic). - `SendValidationTests.swift` — recipient/phone format (hermetic). - `SyncPayloadTests.swift` — Codable round-trips (hermetic). Not covered by tests: chat.db reading, AppleScript sending, contacts permission (all require macOS Privacy permissions).