Résumé

  • Une ContactCard du RFC 9610 appartient toujours à au moins un AddressBook, mais peut relever de plusieurs carnets ; retirer une appartenance n’équivaut donc pas à détruire la carte.
  • Un groupe mémorise les UID de ses membres. Si l’accès à la carte correspondante disparaît, le membre n’est plus affiché, mais l’UID non résolu reste conservé et peut redevenir visible.
  • Pour établir une suppression, il faut le compte, l’identifiant serveur, l’UID, les appartenances antérieures, les arguments de /set, la réponse et une lecture autoritative ultérieure.

La liste vide n’était qu’une observation

Un responsable ferme un carnet créé pour un projet achevé. L’application n’affiche plus aucun contact et l’export d’audit résume l’action par « contacts supprimés ». Quelque temps plus tard, l’un de ces contacts apparaît dans un carnet d’exploitation. L’enregistrement a-t-il été restauré ? Pas nécessairement. La première preuve n’avait peut-être jamais porté sur la destruction de la carte.

Dans le modèle du RFC 9610, l’AddressBook est une collection nommée. Une ContactCard peut appartenir à plusieurs collections. Si le carnet du projet est détruit avec onDestroyRemoveContents à vrai, ses cartes en sont retirées ; seules celles qui n’appartiennent à aucun autre carnet sont détruites. L’écran peut donc être vide, l’opération réussie et la carte encore vivante dans un autre contexte.

La différence sépare trois réalités : ce que l’interface montre, la relation d’appartenance et le cycle de vie de l’objet. Une organisation qui les fusionne fabrique un certificat d’effacement à partir d’un simple résultat de filtre.

Le carnet ne possède pas nécessairement la carte

Un compte JMAP compatible avec les contacts contient des AddressBooks, chacun regroupant des ContactCards. Toute carte vivante doit être rattachée à au moins un carnet. La propriété addressBookIds contient cet ensemble et ne peut devenir vide avant la destruction de l’objet.

Cette structure autorise un contact unique à figurer dans les carnets « Fournisseurs », « Astreinte » et « Renouvellements ». Supprimer la troisième relation ne modifie pas les deux autres. Le protocole ne reprend donc pas automatiquement la métaphore d’une boîte qui posséderait tout son contenu.

C’est une règle commune minimale : la synchronisation standardise les états nécessaires à l’interopérabilité, tandis que le produit reste libre de présenter des carnets, dossiers ou espaces. Mais cette liberté d’interface ne lui donne pas le droit de transformer un retrait local en jugement global sur l’existence de l’objet.

La cascade reste conditionnelle

Par défaut, onDestroyRemoveContents vaut faux. Le serveur refuse alors la destruction d’un carnet non vide avec l’erreur addressBookHasContents. Cette réponse prouve que la demande, avec ces paramètres, n’a pas détruit le carnet ; elle ne dit rien de définitif sur toutes les copies ou tous les futurs états des cartes.

Avec la valeur vraie, le serveur retire le carnet de chaque carte concernée. Il ne détruit une carte que si elle ne relève d’aucun autre carnet. Une seule méthode peut donc détruire la collection, laisser survivre certaines cartes et éliminer les cartes devenues orphelines.

Noter uniquement « cascade demandée » efface le prédicat déterminant. Il faut connaître l’ensemble d’appartenances de chaque carte avant la transition et conserver la réponse effective du serveur. Le nom agressif d’une option n’est pas une preuve de purge universelle.

id et uid ne sont pas deux orthographes du même identifiant

La ContactCard possède un id immuable attribué par le serveur. Elle possède aussi l’uid défini par JSContact ; le RFC indique explicitement que les deux peuvent différer. Dans un même compte, deux ContactCards ne doivent pas partager le même UID.

L’identifiant serveur désigne l’objet visé par les méthodes JMAP. L’UID sert à exprimer la continuité de la carte et les membres d’un groupe. Conserver seulement le nom affiché détruit les deux traces. Conserver seulement l’UID ne dit pas quel objet serveur de quel compte a changé. Conserver seulement l’id perd la référence utilisée par les groupes et les autres contextes.

Un journal fiable garde les deux valeurs sans les confondre avec la personne réelle. La disparition d’une carte dans un compte ne supprime ni les courriels, ni les exports, ni les terminaux, ni les systèmes indépendants, et encore moins la personne décrite.

Le groupe garde une référence que l’écran ne peut plus résoudre

Une carte de type groupe contient un ensemble d’UID. Le client recherche les ContactCards correspondantes dans les comptes auxquels l’utilisateur a accès. Un UID introuvable doit être ignoré lors de l’affichage, mais conservé dans le groupe.

Le RFC 9610 donne précisément le scénario utile : un utilisateur ajoute à un groupe privé des contacts provenant d’un carnet partagé, puis perd temporairement l’accès à ce carnet. Les membres disparaissent du groupe car leurs UID ne se résolvent plus. Quand l’accès revient, les mêmes références retrouvent leurs cartes et les membres réapparaissent.

Il n’y a pas nécessairement restauration. La continuité était présente dans l’UID préservé. Une capture d’écran réalisée pendant l’interruption ne prouve que l’impossibilité de résolution pour ce principal, à cet instant. Elle ne prouve ni la destruction de la carte ni l’absence de visibilité pour un autre acteur.

L’absence d’un résultat de requête a une portée limitée

Le filtre inAddressBook sélectionne les cartes rattachées à un carnet donné. Une carte qui ne correspond pas à ce filtre peut encore exister dans le compte et appartenir à un autre carnet. Le filtre répond à la question posée ; il ne produit pas une preuve d’inexistence universelle.

Les jetons d’état et /changes permettent de suivre les transitions. Encore faut-il conserver l’ancien état, examiner les identifiants créés, mis à jour et détruits, puis résoudre les cas douteux avec /get dans le bon compte. Une ligne qui disparaît d’une vue locale n’est pas un substitut à cette chaîne.

Même une réponse autoritative confirmant la destruction reste bornée : elle établit la fin de la ContactCard dans ce compte JMAP. Elle ne couvre pas les sauvegardes, les autres comptes, les copies, les fichiers exportés ou les applications tierces.

La demande de carnet par défaut rappelle la même discipline

onSuccessSetIsDefault demande au serveur de rendre un carnet par défaut après la réussite des autres changements. Si l’identifiant est absent ou si la politique du serveur refuse le changement, la demande est ignorée sans erreur spécifique et l’ancien défaut demeure.

L’intention soumise n’est donc pas le résultat observé. Le client doit lire les propriétés renvoyées ou interroger l’état courant avant d’annoncer la réussite. Un bouton et une requête documentent une tentative ; seul l’état serveur documente l’effet.

Ce que doit contenir un reçu de suppression

Le reçu commence par le compte JMAP, l’id serveur et l’UID. Il conserve l’ensemble complet des addressBookIds avant l’opération et les éventuelles références d’UID dans des groupes. Il précise si la requête visait le carnet ou la carte, ainsi que la valeur exacte de onDestroyRemoveContents.

Il garde ensuite les champs destroyed, notDestroyed, updated, les erreurs et le nouvel état. Une lecture dans le même compte tranche entre quatre conclusions : retiré de ce carnet ; actuellement non résolu pour ce principal ; objet détruit dans ce compte ; résultat non établi. La formule « la personne a été effacée » dépasse l’autorité du protocole.

Cette précision suit la séparation des couches de réalité : affichage, relation, objet et monde extérieur. Une infrastructure responsable refuse qu’une couche emprunte le vocabulaire d’une autre sans produire son reçu.

Sources