# iMail (`IMailSync`) ## Purpose Read Mail.app messages via AppleScript / ScriptingBridge and stream them to the server. ## Direction Read-only in practice (Mac to server). The `Sender` is implemented but unwired — see [known-limitations](../known-limitations.md#imail-sender-is-unwired). ## OS surface Mail.app via ScriptingBridge. The `Package.swift:64-66` linker setting pulls in `ScriptingBridge.framework`. Reader runs synchronously on a background thread (`@packages/imail/Sources/IMailSync/SyncManager.swift:90-93`). Requires Automation > Mail permission. ## Files - `Reader.swift` — `Reader.shared`; `fetchMessages(since:)` traverses Mail.app accounts, mailboxes, and messages. Applies a ±1-minute watermark tolerance (documented at `SyncManager.swift:40-43`). - `MIMEParser.swift` — minimal MIME header decoder used by the Reader. - `APIClient.swift`: - `syncMail(_:)` -> `POST /client/imail/sync` (`@packages/imail/Sources/IMailSync/APIClient.swift:86-88`) - `getStats()` -> `GET /client/imail/stats` (line 102-103) - `Sender.swift` — `Sender.send(_:)` drives Mail.app via ScriptingBridge. - `SendQueueAdapter.swift` — `IMailSendTransport` + `MailSender` + dispatch closure. Used by the `SyncManager`'s `SendQueueClient` to drain `/client/imail/send-queue/pending`. - `APIClient.swift` — `getPendingSends()` / `reportSendResult(...)` added alongside the existing read endpoints. - `SyncManager.swift` — reads off-main, batches 50; owns a lazy `SendQueueClient` started via `didStartSync()` and stopped via `willStopSync()`. ## Timing - Read interval: **300s** (`@packages/imail/Sources/IMailSync/SyncManager.swift:61`). - Outbound `SendQueueClient`: **60s**. - Batch size: 50. ## Server surface - Entity tables: existing mail-ingest tables live under `features/imail/`; outbound table is `icloud.mail_send_queue` (`src/server/src/entities/mailSendItem/schema.ts`), built on the shared `sendQueueTableSql` factory. - Client routes (`src/server/src/surfaces/client/imail.ts`): - `GET /client/imail/stats` - `POST /client/imail/sync` - `GET /client/imail/send-queue/pending` - `POST /client/imail/send-queue/:id/result` - Web routes (`src/server/src/surfaces/my/mail.ts`): - `GET /my/mail/conversations` - `GET /my/mail/conversations/:id/messages` - `GET /my/mail/last-sync` - `POST /my/mail/send` — enqueues a `send_mail` action - Admin route (`src/server/src/surfaces/admin/mail-send-queue.ts`): - `POST /admin/mail-send-queue/enqueue` ## Web surface - Tab: `/mail` (`web/src/App.tsx:59`). - API helpers: `web/src/api/mail.ts`. ## Known limitations - **AppleScript / ScriptingBridge fragility.** The reader runs in a detached `Task` but a hung Mail.app blocks until macOS times the Apple event out. The send path inherits the same constraint. - **No folder browser** in `/my/mail`; only conversations and messages by ID. - **No CC/BCC UI** in the web Compose form yet (the server payload schema and Sender both accept them; only the web form is missing them). ## Tests (`@packages/imail/Tests/IMailSyncTests/`) - `MIMEParserTests.swift` — header decoding (hermetic). - `SenderTests.swift` — `MailSender` dispatch with a `FakeApplier`: success path, unknown-action failure, error propagation. Not covered by tests: Mail.app reading, ScriptingBridge bindings, live send.