mac: ship entitlements — sandbox on, keychain group, communication notifications (#474)
The scaffold-era empty entitlements file deferred the App Sandbox until the app had a real signing identity; it has one now (Developer ID + notarization via release-mac.sh), so this turns the ship set on: sandbox, network client, user-selected read-write (uploads + save panels), the app-family keychain group, and the communication notifications entitlement that lights up sender faces on Mac banners (CommunicationNotification.swift has been wired and dormant since the iOS feature landed). Debug mirrors the set so dev builds run under the same sandbox they ship with. Sparkle under sandbox installs through its bundled InstallerLauncher XPC service (SUEnableInstallerLauncherService); its Downloader stays off since the app holds network.client. No migration needed: Keychain.swift always used the data-protection keychain and reads group-first-then-ungrouped, so existing identities survive, and everything on disk is a rebuildable cache. Verified: Debug (Apple Development) and developer-id export both mint profiles carrying the restricted entitlements — communication notifications IS grantable for macOS App IDs — with hardened runtime intact.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
3 files changed,
+48
−5
ios/LChatMac/LChatMac-Dev.entitlements+14 −0ios/LChatMac/LChatMac.entitlements+25 −5ios/project.yml+9 −0
diff --git a/ios/LChatMac/LChatMac-Dev.entitlements b/ios/LChatMac/LChatMac-Dev.entitlements| index 1896b87..677aa7d 100644 |
| --- a/ios/LChatMac/LChatMac-Dev.entitlements |
| +++ b/ios/LChatMac/LChatMac-Dev.entitlements |
| @@ -13,5 +13,19 @@ |
| <array> |
| <string>webcredentials:home.elevenmessenger.com</string> |
| </array> |
| + <!-- Everything below mirrors LChatMac.entitlements (the ship set) so dev |
| + builds run under the same sandbox they'll ship with. --> |
| + <key>com.apple.security.app-sandbox</key> |
| + <true/> |
| + <key>com.apple.security.network.client</key> |
| + <true/> |
| + <key>com.apple.security.files.user-selected.read-write</key> |
| + <true/> |
| + <key>keychain-access-groups</key> |
| + <array> |
| + <string>$(AppIdentifierPrefix)com.elevenmessenger.messenger.shared</string> |
| + </array> |
| + <key>com.apple.developer.usernotifications.communication</key> |
| + <true/> |
| </dict> |
| </plist> |
diff --git a/ios/LChatMac/LChatMac.entitlements b/ios/LChatMac/LChatMac.entitlements| index d96be57..3adfc8d 100644 |
| --- a/ios/LChatMac/LChatMac.entitlements |
| +++ b/ios/LChatMac/LChatMac.entitlements |
| @@ -2,10 +2,30 @@ |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| <plist version="1.0"> |
| <dict> |
| - <!-- Scaffold build: no App Sandbox, so the traditional Keychain persists the |
| - chat list under ad-hoc signing (the data-protection keychain would need a |
| - team-scoped keychain-access-group entitlement we can't mint ad-hoc). A ship |
| - build should re-enable the sandbox with a real signing identity, at which |
| - point the data-protection keychain works and this can go back on. --> |
| + <!-- The ship entitlements the scaffold build deferred: sandbox on, real |
| + keychain group, communication notifications. The restricted ones |
| + (keychain group, communication) ride the provisioning profile that |
| + automatic signing embeds at archive time. Existing installs keep their |
| + identity across this change: Keychain.swift always used the |
| + data-protection keychain and reads group-first-then-ungrouped, and |
| + everything the app keeps on disk is a rebuildable cache. --> |
| + <key>com.apple.security.app-sandbox</key> |
| + <true/> |
| + <key>com.apple.security.network.client</key> |
| + <true/> |
| + <!-- read-write: attachment uploads read picked files; "Save" panels write |
| + downloads — both user-initiated through the powerbox. --> |
| + <key>com.apple.security.files.user-selected.read-write</key> |
| + <true/> |
| + <!-- Same app-family group as iOS: the saved-instances store |
| + (LChatCore/Keychain.swift) writes here once the entitlement exists. --> |
| + <key>keychain-access-groups</key> |
| + <array> |
| + <string>$(AppIdentifierPrefix)com.elevenmessenger.messenger.shared</string> |
| + </array> |
| + <!-- Communication notifications: banner restyle via INSendMessageIntent — |
| + sender's photo/monogram as the icon (CommunicationNotification.swift). --> |
| + <key>com.apple.developer.usernotifications.communication</key> |
| + <true/> |
| </dict> |
| </plist> |
diff --git a/ios/project.yml b/ios/project.yml| index 2f74be0..84ff63d 100644 |
| --- a/ios/project.yml |
| +++ b/ios/project.yml |
| @@ -158,6 +158,15 @@ targets: |
| # Skips Sparkle's own first-run "should Eleven check for updates |
| # automatically?" prompt — background checking is the default we want. |
| SUEnableAutomaticChecks: true |
| + # The app is sandboxed (LChatMac.entitlements), so Sparkle installs |
| + # updates through its bundled InstallerLauncher XPC service instead of |
| + # spawning the installer directly. Its Downloader service stays off — |
| + # the app has com.apple.security.network.client, which Sparkle prefers. |
| + SUEnableInstallerLauncherService: true |
| + # Communication notifications (sender's photo/monogram on banners): |
| + # declared alongside the entitlement, matching the iOS target. |
| + NSUserActivityTypes: |
| + - INSendMessageIntent |
| # Notification service extension: decrypts pushed ciphertext on-device (the |
| # E2EE half of APNs). Shares the crypto/keychain code with the app via LChatCore. |
| LChatNSE: |