> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oximail.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# Partage

> La surface JMAP Sharing (RFC 9670) : Principal (l'annuaire), les droits shareWith, ShareNotification, l'indirection shared:, et les grants de fédération inter-organisations.

Le partage implémente la [RFC 9670](https://www.rfc-editor.org/rfc/rfc9670) (`urn:ietf:params:jmap:principals`) : un annuaire de **Principals** (les personnes, équipes, ressources et lieux de l'organisation) et un modèle de droits uniforme que chaque type de collection réutilise.

## Objets et méthodes

| Objet               | Méthodes                                         | Notes                                                                                                        |
| ------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `Principal`         | `get`, `set`, `query`, `queryChanges`, `changes` | L'annuaire : `individual`, `group`, `resource`, `location`, `other`.                                         |
| `ShareNotification` | `get`, `set`, `query`, `queryChanges`, `changes` | Les événements « X a partagé Y avec vous », du plus récent au plus ancien, supprimables par le destinataire. |

## Le modèle de droits

Chaque collection partageable (Mailbox, Calendar, TaskList, AddressBook, FileNode, Label) porte la même carte `shareWith` : identifiant de principal vers objet de droits (`mayRead`, `mayWrite`, `mayAdmin`, plus des droits de domaine comme `mayReadFreeBusy`). Les droits sont :

* **Écrits par le propriétaire** sur l'objet collection ; chaque changement de droit produit une `ShareNotification` pour le bénéficiaire et un changement enregistré pour la resynchronisation.
* **Exercés par l'indirection `shared:`** : un bénéficiaire adresse les données du propriétaire avec l'identifiant de compte accordé dans le slot `accountId` JMAP standard ; l'objet `accounts` de la session liste ce qui est joignable. Les capacités se résolvent sur **l'appelant authentifié**, la résidence des données sur **le propriétaire** : l'usurpation n'est pas représentable dans la forme de la requête.
* **Fermés par défaut.** Le dispatch des comptes partagés refuse par défaut ; une erreur de recherche de droits refuse plutôt que d'accorder.

## Sémantique d'annuaire à connaître

* **Les types de principals sont honnêtes.** Le principal d'un compte d'équipe partagé est `type: "other"` (non connectable), jamais `individual` : les sélecteurs de personnes peuvent se fier au type.
* **Les bénéficiaires doivent être adossés à un humain.** Un droit accordé à un principal adossé à un compte non connectable est rejeté à l'écriture sur tous les domaines.
* **Détruire un principal purge ses droits.** Un destroy de `Principal/set` retire les clés du bénéficiaire de chaque carte `shareWith` (pas d'entrées d'ACL pendantes) et avertit ou s'interrompt s'il laisserait un drive partagé sans gardien éligible.
* **L'effacement laisse des pierres tombales.** Le principal d'une personne effacée est nettoyé de ses données personnelles mais conservé comme ancre de graphe, et un principal en pierre tombale est refusé comme nouveau bénéficiaire ([conformité](../operator/compliance)).
* **`Principal/getAvailability`** (le libre/occupé) est documenté sur la [page agenda](./jmap-calendar).

`ShareNotification/query` renvoie un ordre total (du plus récent au plus ancien). Les règles de rigueur sont identiques à la [surface courrier](./jmap-mail).

## D'une organisation à l'autre : la fédération

Tout ce qui précède partage des données *à l'intérieur* d'une organisation. Partager avec une organisation **différente** est une surface distincte, `urn:oximail:params:jmap:federation`, et ce n'est volontairement pas le même mécanisme : un grant fédéré n'est ni une entrée `shareWith`, ni une ligne d'accès au compte. En v0.30.0, ce qu'il porte, c'est la **disponibilité** (le libre/occupé) : le grant élargit *qui* peut demander, jamais *ce* qui est visible.

L'objet de capacité annonce le vocabulaire au lieu de laisser les clients le découvrir par des refus :

```json theme={null}
"urn:oximail:params:jmap:federation": {
  "supportedGranteeKinds": ["realm"],
  "revocationIsForwardOnly": true
}
```

| Objet                  | Méthodes              | Notes                                                                                                                  |
| ---------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `FederationPeer`       | `get`, `query`        | En lecture seule. Les organisations appairées, dans l'ordre des realms : de quoi remplir un sélecteur de bénéficiaire. |
| `FederationGrant`      | `get`, `set`, `query` | Les enregistrements de consentement propres au sujet.                                                                  |
| `FederationDisclosure` | `get`, `query`        | En lecture seule. Ce que chaque pair s'est effectivement vu dire au sujet de cette personne.                           |

### Le bénéficiaire est une organisation, pas une personne

`supportedGranteeKinds` vaut `["realm"]`, et ce n'est pas une question de calendrier. L'identité qu'une requête fédérée prouve aujourd'hui est le **nœud** émetteur, pas lequel de ses principals a agi. Un grant nommant une seule personne à l'autre bout ne serait donc pas réellement plus étroit qu'un grant nommant son organisation : il en aurait seulement *l'air* dans une interface. La liste existe pour que, le jour où une couche d'identité par principal arrivera, `"principal"` soit un ajout que le client peut détecter, et non un changement de comportement à deviner.

`granteeValue` doit être un realm avec lequel vous avez réellement été appairé. L'appairage lui-même est une cérémonie d'opérateur bilatérale et hors bande : ce n'est pas une méthode JMAP, et c'est précisément pourquoi `FederationPeer` existe. Sans cette liste, un client n'avait que deux options, mauvaises toutes les deux : un champ libre où presque toute saisie est rejetée, ou un sélecteur que rien ne remplit.

`FederationPeer` est **borné à l'organisation et lisible par tout compte authentifié** de celle-ci, et pas seulement par les administrateurs : un appairage est une relation de l'organisation, sans vue par sujet. L'objet porte `realm` (la valeur à envoyer en `granteeValue`), `jurisdiction`, `state` et `pairedAt`. Il n'expose volontairement ni la clé épinglée, ni l'ancre d'appairage, ni l'époque de clé, ni la cause de suspension : ce sont des écritures d'opérateur, et une clé publique sur chaque écran de consentement serait de la surface de sécurité achetée sans aucune fonctionnalité en échange.

<Note>
  `FederationPeer` n'a pas de méthode `changes`, et son `state` n'est **pas** un compteur de journal de changements : c'est une empreinte de la liste servie. L'appairage et la suspension se font dans des cérémonies d'opérateur, hors de toute requête JMAP, donc rien n'écrit de ligne de journal pour elles ; un état de journal serait ici une chaîne qui ne bouge jamais alors que les données dessous changent.
</Note>

### Le consentement ne se délègue pas

* **Seul le sujet peut créer ou modifier un grant**, en agissant sur son propre compte. Un administrateur peut **révoquer**, car la révocation retire un accès et un opérateur qui doit couper une fuite n'a pas à attendre le sujet, mais jamais accorder à la place de quelqu'un. Un grant qu'un administrateur peut créer pour vous n'est pas un consentement. Le contrôle est plus strict qu'un test de rôle : une requête qui porte un accès partagé, c'est quelqu'un qui agit pour un autre, quel que soit son niveau.
* **Chaque écriture est une seule transaction** : la ligne de grant, la `ShareNotification` qui prévient le sujet et la ligne d'audit qui nomme *l'auteur de l'écriture* valident ensemble. L'audit enregistre qui a réellement écrit, pas le compte que la requête visait : la révocation d'un administrateur n'est donc jamais attribuée au sujet.
* **Un `granteeKind` non pris en charge est refusé bruyamment.** `"principal"` s'analyse comme une requête bien formée et aurait l'air accepté ; la ligne créée serait un grant de realm portant le nom d'une personne.

L'essentiel d'un grant est décidé par le serveur et en lecture seule sur le fil : seuls `granteeKind`, `granteeValue` et `expiresAt` sont réglables. `rights` en particulier ne l'est pas : un client capable d'élargir son propre enregistrement de consentement au-delà de ce que le résolveur honore serait une permission déguisée en réglage. `expiresAt: null` signifie que le sujet a explicitement choisi « sans expiration », et `jurisdiction` est **gelée à la création** : le consentement a été donné à une contrepartie dans un lieu donné, et un champ modifiable laisserait le lieu changer sans nouveau consentement.

### Pourquoi un grant peut cesser d'agir, et comment le sujet l'apprend

`FederationGrant.state` répond directement à « pourquoi cette organisation ne me voit-elle plus ? » : `active`, `unpaired`, `expired`, `dormancy_exhausted`, `dormant`, `lapsed` ou `reanchored`. Le stockage le dérive du prédicat même qu'applique la lecture d'autorisation : il ne peut donc pas s'écarter de la décision. `FederationPeer.state` (`active`, `dormant`, `lapsed`) emploie les mêmes mots pour les mêmes défaillances, et seul `active` accepte un grant, là encore dérivé du prédicat sur lequel l'écriture refuse : un realm que le sélecteur propose est un realm que l'écriture accepte.

Deux conséquences valent la peine d'être prises en compte dès la conception :

* **La révocation est unilatérale, immédiate, et ne vaut que vers l'avant.** Un pair ne lit ni n'écrit jamais vos grants ; il apprend une révocation en se voyant refusé. C'est ce qui garde la révocation hors du terrain de la cohérence distribuée, et c'est aussi pourquoi `revocationIsForwardOnly` est annoncé comme donnée : révoquer retire le chemin de lecture pour la suite et ne défait rien de ce qui a déjà été divulgué.
* **Le journal des divulgations est l'autre moitié de cette phrase.** `FederationDisclosure` est ce qui rend « révoquer ne défait rien de déjà divulgué » vérifiable au lieu de simplement rassurant : il survit au grant, et il n'existe **aucun** `FederationDisclosure/set`, pas même limité au destroy. C'est la rétention qui borne le journal ; le sujet n'en efface rien. Il enregistre les **divulgations, jamais les tentatives** : un pair qui sonde cinq cents adresses ne crée aucune ligne au sujet des quatre cent quatre-vingt-dix-neuf personnes qui n'ont jamais consenti. Chaque ligne nomme le realm du pair, la couche d'identité vérifiée, la juridiction sous laquelle le grant se tenait à cet instant, et la date.

<Note>
  Les filtres et les tris sur les deux méthodes `query` sont **refusés** (`unsupportedFilter` / `unsupportedSort`) plutôt qu'ignorés en silence. Un client qui filtrerait les pairs sur `state: "active"` et recevrait discrètement aussi les dormants proposerait à son utilisateur un bénéficiaire que l'écriture rejette ensuite.
</Note>
