Find My on macOS 14.4+ via cache decryption (+ LAN URL multi-IP picker) - #810
Find My on macOS 14.4+ via cache decryption (+ LAN URL multi-IP picker)#810PnutCN wants to merge 2 commits into
Conversation
…he FindMyInterface class to support plist file parsing and data decryption
Apple started encrypting the Find My location cache (and the Private API helper stopped working) in macOS 14.4. Read locations by decrypting the cache instead of relying on Private API injection. - decrypt FMIP/FMF caches (ChaCha20-Poly1305) and LocalStorage.db (AES-256 keystream XOR + WAL) to get device & friend locations - add FindMyKeyManager + a key-import UI (folder picker) for the three keys - version-aware via new isMinSonoma14_4 flag: 14.4+ uses decryption, 11-14.3 keep the existing Private API path; GET /findmy/friends no longer forces the helper on 14.4+ - friend payload matches the legacy shape (is_locating_in_progress is a boolean); address fields are null (not persisted in the cache) - ui: LAN URL picker so hosts with multiple local IPs can choose/copy the reachable address
|
For the FindMy part, it fetches and reads from the DB using the decryption keys, but does it also poll for changes? We are trying to fix the findmy implementation in the private API plugin, and i wonder if this can be the baseline, while the papi helper does the live updates. Trying to figure out how to incorporate it. |
No, the current implementation doesn't poll for changes — it's purely on-demand read-from-disk: every time GET /findmy/friends or refreshFriends() is called, it re-decrypts the cache files (LocalStorage.db + WAL) and re-reads the FMIP .data files. There's no periodic watcher or polling loop. The approach is "the OS writes the cache, we just read it at request time." Your idea of using the decryption as baseline + PAPI for live updates makes total sense. They're actually complementary: Decryption path (my code): provides the last-known locations even when PAPI is broken/busy, with zero code injection. Handles macOS 14.4+ where the PAPI helper hook no longer fires for Find My. |
Find My on macOS 14.4+ via cache decryption (+ LAN URL multi-IP picker)
Summary
On macOS 14.4+, Apple encrypts the Find My location cache and the Private API helper no longer works for Find My. As a result, the Find My Devices and Friends endpoints stopped returning data on recent macOS versions.
This PR makes Find My work again on macOS 14.4 / 15 (Sequoia) / 26 by reading and decrypting the local Find My cache directly, instead of relying on Private API injection. It is fully version-aware: older macOS (11–14.3) keeps the existing Private API path unchanged.
It also includes a small, unrelated UX fix for the LAN URL proxy option on machines with multiple local IPs.
Motivation
LocalStorage.db(a custom AES-256 keystream-XOR SQLite codec).What's changed
Find My decryption (macOS 14.4+)
api/lib/findmy/decrypt/cache.ts— decrypts FMIP/FMF.datacaches (ChaCha20-Poly1305) for Devices, Items, and friend display names. Normalizes Apple's"$null"placeholders back to realnull.localStorage.ts— decryptsLocalStorage.db(+ WAL) using Apple's per-page AES-256 keystream-XOR codec to obtain friend coordinates.localStorageReader.ts— opens the decrypted DB (better-sqlite3) and joinssecureLocations(coordinates) withfriends(handle).fmfReader.ts— pulls friend display names from the FMF cache.FindMyKeyManager.ts— loads/validates/imports the three decryption keys.findMyInterface.ts— version-aware strategy and friend payload assembly; output shape unchanged.Key management UI
LocalStorage.key,FMIPDataManager.bplist,FMFDataManager.bplist) produced by findmy-key-extractor. Keys are stable across reboots, so this is a one-time setup.get-findmy-keys-status,import-findmy-keys.Version awareness
isMinSonoma14_4env flag (14.4 is where encryption was introduced / the helper broke).GET /findmy/friendsno longer forces the Private API helper to be connected on 14.4+ (it reads the decrypted cache instead). 11–14.3 still require the helper as before.LAN URL multi-IP picker (UX)
API compatibility
Verified against the app's models (
findmy_device.dart,findmy_friend.dart) and the Find My page (findmy_page.dart):is_locating_in_progressis a boolean (the app field isbooland crashes on0),last_updatedis ms epoch,statusis one oflegacy/shallow/live, coordinates are[lat, lng].null(the cache only stores coordinates; the Find My app reverse-geocodes at display time)."$null"placeholder for empty values (including a bare"$null"foraddress). These are normalized to realnull, otherwise the app callsAddress.fromJson("$null")(a String) and crashes the whole device list.ValueKey(address.uniqueValue)whereuniqueValue = label ?? mapItemFullAddress. Co-located devices (e.g. several devices at the same home address) produce identical keys, which crashes/blanks the device list on rebuild. As a server-side workaround, duplicate address keys within a list group are made unique by appending zero-width spaces (U+200B) — unique key, visually identical. The proper fix is app-side (ValueKey(item.id)).Version / behavior matrix
Requirements / notes
@noble/ciphersandbplist-parserdependencies (required by the decryption code).Testing
Screenshots
Find My key import card:

Friends & devices on the app:


LAN URL picker:
