4.3 KiB
4.3 KiB
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().getMessagesnow invokesextractTextFromAttributedBody(_:)(line 371): when chat.db'stextcolumn is null/empty andattributedBodyis 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.SyncManager.swift—final class SyncManager: BaseSyncManager<SyncStats, SyncError>(line 44); 30s timer (line 93). OwnssendQueueClient: SendQueueClient<IMessageSendTransport>(lines 70-86),didStartSync/willStopSync(lines 170-176). Handles schema-version migrations viaSelf.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 driverSendService; usesAppleScriptEscape(@packages/shared/Sources/MacSyncShared/Util/AppleScriptEscape.swift:13) for all string interpolation.SendQueueAdapter.swift—IMessageSendTransportadapting the bespoke legacy send-queue endpoints into the genericSendQueueTransportprotocol.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
attributedBodyis forwarded raw; macOS 13+ messages with no plaintext appear blank in the dashboard. See known-limitations.- 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
<module>_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).