macsync/docs/known-limitations.md

5.1 KiB

Known limitations

Honest list of where @mac-sync is fragile or incomplete. Verified against the code as of the current tree.

iMessage attributedBody decoder is heuristic, not a full typedstream parser

Reader.swift:371 (extractTextFromAttributedBody) is now wired into the row extraction loop: when chat.db's text column is null/empty AND attributedBody is non-nil, the byte-scanning heuristic recovers plain text from the NSKeyedArchiver typedstream and uses it as the message body. This covers most URL bubbles, reactions, and expressive sends.

What it does NOT do:

  • Decode rich formatting (bold/italic/colors). Only the raw text run is extracted.
  • Handle multi-run NSAttributedStrings — only the first contiguous text run with an NSString marker is returned. Multi-paragraph rich messages may lose later paragraphs.
  • Decode binary attachments embedded in the blob.

The full typedstream decoder on the server side is a stub at src/server/src/shared/typedstream/decode.ts that returns null. When implemented, it will populate icloud.messages.body_decoded for messages where the Mac-side heuristic failed or where historical data needs a backfill pass. The schema (attributed_body BYTEA + body_decoded TEXT columns + combined FTS tsvector) is already in place.

iPhoto has no Sender

By design (~/.claude/plans/magical-tumbling-peach.md:184): there is no 2-way Photos sync. iPhoto is Mac-to-server only. The dashboard cannot upload photos to the Mac library. Photos.framework's write API is awkward and the user explicitly scoped this out.

AppleScript fragility

Three modules talk to macOS apps via AppleScript: iMessage (Messages.app), iMail (Mail.app via ScriptingBridge), and iNotes (Notes.app). Known failure modes:

  • iNotes attachments do not round-trip. AppleScript's note body is HTML, but inline images and Apple Pencil drawings are stored out-of-band. The Notes Sender writes plain HTML only. Existing attachments stay; new ones cannot be created from the web.
  • Notes formatting fidelity is lossy. Bulleted lists, code blocks, and checkboxes survive a round-trip; pinned/locked state, tags, and per-paragraph styles may not.
  • Permission prompts. First run of each AppleScript module triggers System Settings > Privacy > Automation. If the user denies, the module's onAuthorizationDenied() hook sets the per-module error (e.g. noteAccessRequired, automationDenied) and the cycle bails out silently (@packages/inotes/Sources/INoteSync/SyncManager.swift:91-94).
  • iMessage AppleScript can hit Messages.app stalls. The SendService layer (@packages/imessage/Sources/IMessageSync/Sender.swift) wraps the AppleScript call with rate limits and delivery tracking but cannot recover from a hung Messages process — manual restart is required.

Watermark is cycle-start time, not record time

BaseSyncManager.runCycle sets lastSync = Date() at the end of a successful cycle (@packages/shared/Sources/MacSyncShared/Sync/BaseSyncManager.swift:126-127). If the OS produces records faster than the cycle runs, edge-of-window records can be missed. iMail's Reader compensates with a ±1-minute tolerance (@packages/imail/Sources/IMailSync/SyncManager.swift:40-43); other modules do not. In practice this only matters for high-frequency streams, which only iMessage really has (30s tick mitigates it).

Conflict resolution: last-write-wins

Per the plan (~/.claude/plans/magical-tumbling-peach.md:182):

iCloud-side conflict resolution for simultaneous Mac+web edits — last-write-wins via modifiedAt. Document in code, not a feature.

If you edit the same event from Calendar.app and from the web within the same cycle, whichever write completes second wins. There is no operational transformation, no CRDT, no merge UI.

iMessage send queue is on a bespoke schema

The legacy icloud.send_queue table predates the generic createSendQueueRepo factory (src/server/src/entities/send-queue/schema.ts:5-23). It has extra columns (send_queue_id, custom status enum) that the factory does not model. This is intentional — see plan line 12 and architecture invariant 2 — but it means tooling (admin dashboards, retry scripts) must special-case the iMessage queue.

IPhoto.Stats.uploadRate is per-session only

IPhotoSyncStats.uploadStartTime resets on every cold start (@packages/iphoto/Sources/IPhotoSync/SyncManager.swift:14-46). The dashboard shows live throughput during a session but cannot report historical upload speeds.

iMail /my/mail exposes no folder browser

Only /conversations, /conversations/:id/messages, and /last-sync exist (surfaces/my/mail.ts:10-25). The web tab cannot drill into folders by name.

Server has no auth bootstrap for fresh devices

POST /client/devices/register is exempt from deviceTokenAuth (src/server/src/app/server.ts:69-71). Anyone on the LAN who can reach :3201 can register a device. The SSO middleware on /my/* protects the web side, but /client/* relies on network isolation. If you expose :3201 to the public internet, register-spam is a real risk.