eleven / messenger Public

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>
Paul Campbell 2c18078fee9d2f15103126724666b3fad9a940e7 parent 05bf161
3 files changed, +48 −5
  • ios/LChatMac/LChatMac-Dev.entitlements +14 −0
  • ios/LChatMac/LChatMac.entitlements +25 −5
  • ios/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: