La saga de la signature du bac à sable macOS (ou pourquoi les v1.4.0 à v1.4.5 existent)
Si vous avez regardé notre changelog aujourd'hui et remarqué six versions correctives dans la même journée, vous vous demandez peut-être ce qui s'est passé. Réponse courte : la signature de code macOS et l'App Sandbox ont failli avoir notre peau. Voici l'histoire complète.
Comment MailVault fonctionne sous le capot
MailVault est une application de bureau Tauri v2. Le frontend est du React qui tourne dans une webview native, et le backend est un serveur Node.js Express compilé en binaire natif avec Bun. Ce binaire, appelé « sidecar », s'exécute aux côtés de l'application principale et gère toutes les communications IMAP/SMTP.
Pour la distribution sur macOS, Apple exige que les applications soient signées avec un certificat Developer ID et notariées. Nous activons également l'App Sandbox — la couche de sécurité d'Apple qui restreint ce à quoi l'application peut accéder sur votre système. Elle est obligatoire pour le Mac App Store et c'est une bonne pratique pour toute application de bureau.
Le problème
Après la sortie de la v1.4.0, nous avons découvert que le serveur sidecar ne démarrait pas dans les versions de production. L'application se lançait bien, mais le moteur plantait silencieusement avec le code de sortie 133 (SIGTRAP). Tout marchait parfaitement en développement — seule la version signée et en bac à sable était touchée.
Il a fallu plusieurs itérations pour peler les couches :
- v1.4.0 — le serveur écoutait sur
0.0.0.0alors que le frontend se connectait àlocalhost, d'où une incompatibilité IPv4/IPv6. Corrigé en écoutant explicitement sur127.0.0.1. - v1.4.1 — la fenêtre « Ajouter un compte » utilisait un
fetch('/api/test-connection')brut au lieu de l'assistant d'API. Dans les builds de release Tauri, l'origine de la webview esttauri://localhost, et nonhttp://localhost— les URL relatives pointent donc au mauvais endroit. - v1.4.2 — l'entitlement
com.apple.security.network.servermanquait. Le bac à sable empêchait le sidecar d'écouter sur un port. - v1.4.3 — nous avions un nettoyage de processus fondé sur le shell (
lsof,kill), bloqué à l'intérieur de l'App Sandbox. Remplacé par le handleCommandChildde Tauri. - v1.4.4 — le vrai coupable : les binaires compilés avec Bun ont besoin du JIT (compilation à la volée). Le compilateur JIT alloue de la mémoire exécutable à l'exécution. macOS dispose bien d'entitlements pour l'autoriser (
com.apple.security.cs.allow-jit), mais Tauri signe tous les binaires du bundle avec les mêmes entitlements, au moyen de--deep. Le sidecar se retrouvait avec les entitlements App Sandbox, qui bloquaient le JIT même en présence du drapeau allow-jit. - v1.4.5 — correction de la chaîne de signature. Le sidecar est désormais signé séparément, avec ses propres entitlements (pas d'App Sandbox, JIT autorisé). L'application principale conserve l'App Sandbox. L'ordre de signature compte : le sidecar d'abord, puis le bundle de l'application principale sans
--deep, afin de préserver les entitlements du sidecar. Nous avons aussi découvert que notre chaîne CI envoyait le DMG non signé généré automatiquement par Tauri, au lieu du nôtre, correctement signé.
Le correctif
La solution a deux volets :
- Des entitlements séparés — le sidecar reçoit
entitlements-sidecar.plist(JIT + réseau, pas de bac à sable). L'application principale reçoitentitlements.plist(bac à sable + JIT + réseau). - Le bon ordre de signature — signer d'abord le sidecar avec ses propres entitlements, puis signer le bundle
.appprincipal sans--deep. Avec--deep, tout le contenu du bundle serait re-signé récursivement, écrasant les entitlements du sidecar.
Comment vérifier la signature vous-même
Si vous voulez vérifier que MailVault est correctement signé et notarisé sur votre machine, ouvrez le Terminal et lancez ces commandes :
1. Vérifier la signature de l'application :
codesign --verify --verbose /Applications/MailVault.app
Vous devriez voir :
/Applications/MailVault.app: valid on disk /Applications/MailVault.app: satisfies its Designated Requirement
2. Contrôler les autorisations de l'application principale :
codesign -d --entitlements - /Applications/MailVault.app
Cherchez com.apple.security.app-sandbox à true — cela confirme que l'application s'exécute dans un bac à sable.
3. Contrôler les autorisations du sidecar :
codesign -d --entitlements - /Applications/MailVault.app/Contents/MacOS/mailvault-server
Le sidecar doit avoir com.apple.security.cs.allow-jit mais ne doit pas avoir com.apple.security.app-sandbox.
4. Vérifier la notarisation :
spctl --assess --type execute -v /Applications/MailVault.app
Vous devriez voir :
/Applications/MailVault.app: accepted source=Notarized Developer ID
5. Vérifier la notarisation du DMG (avant installation) :
spctl --assess --type open --context context:primary-signature -v ~/Downloads/MailVault-v1.4.5.dmg
Les leçons retenues
- Testez les builds de release tôt. Les builds de développement sautent complètement la signature de code et le bac à sable. L'écart entre « ça marche en dev » et « ça marche en release » peut être énorme.
- Les sidecars demandent un traitement à part. Si vous embarquez un binaire compilé avec Bun, Node ou Deno dans une application Tauri, il lui faut ses propres entitlements. Tauri ne gère pas nativement des entitlements par binaire — il faut re-signer après le build.
--deepest dangereux. C'est pratique, mais cela écrase toutes les signatures imbriquées avec les mêmes entitlements. Si des binaires différents ont besoin d'entitlements différents, signez-les un par un.- Un seul script de build pour le local et la CI. Avoir une logique de signature distincte pour les builds locaux et pour la CI a conduit la CI à envoyer le mauvais DMG, celui qui n'était pas signé. Les deux utilisent désormais le même script.
Si vous avez été touché par ces versions cassées, nous nous excusons pour la gêne occasionnée. La v1.4.5 est la version stable et correctement signée. Vos e-mails n'ont jamais été en danger — le problème portait uniquement sur le binaire de l'application, qui ne lançait pas son serveur.