> ## 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.

# Extensions JMAP v2

> La surface v2 modernisée d'OxiMail telle qu'elle est servie aujourd'hui : négociation par capacité, alias de méthodes à double capacité, et les objets de capacité v2 (étiquettes, règles, conversations, administration, limites de ressources...).

À côté des capacités JMAP standard, OxiMail sert une famille de **capacités d'extension v2** sous `urn:oximail:params:jmap:v2:*`. Cette page décrit la surface **telle que construite** : ce que le serveur négocie et sert réellement en v0.30.0. Les extensions sont en cours de rédaction comme Internet-Drafts destinés à l'IETF ; voir [drafts IETF](../reference/ietf-drafts) pour le programme.

## Comment v2 se négocie

v2 n'est pas un commutateur de version : c'est un **opt-in par capacité**, exactement comme n'importe quelle capacité JMAP :

* Un client demande une capacité v2 dans `using` ; les méthodes qu'elle déverrouille deviennent appelables dans cette requête. Rien ne change au comportement v1 pour les clients qui ne demandent jamais.
* Les noms de méthodes sont des **alias à double capacité** quand une capacité v1 et une v2 couvrent le même handler : le registre fait correspondre les deux écritures à une seule implémentation, il y a donc exactement un comportement par méthode, quelle que soit la capacité qui l'a déverrouillée.
* L'**ensemble d'exclusion mutuelle est vide** en v0.30.0 : associer une capacité v1 à son homologue v2 dans la même requête est permis (voir [JMAP Core](./jmap-core) pour le mécanisme).
* Comme partout ailleurs, **l'objet de session fait foi** : l'entrée `accountCapabilities` de chaque compte pour un URN v2 porte les limites et options annoncées de cette capacité. Détectez les fonctionnalités depuis la session, jamais depuis la version du serveur.

## Les objets de capacité v2

| Capacité (`urn:oximail:params:jmap:v2:...`) | Déverrouille                                                                                                                                                                                                  |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mail`                                      | La forme courrier modernisée sur `Email/get` / `Email/set` (servie en pont à double capacité avec la RFC 8621).                                                                                               |
| `labels`                                    | `Label/get`, `set`, `query`, `changes` : les étiquettes utilisateur comme objets de premier rang avec `shareWith`, distinctes des mots-clés.                                                                  |
| `rules`                                     | `Rule/get`, `set`, `query`, `changes`, `apply`, `test` : la couche de filtrage structurée ([règles & Sieve](../operator/sieve-rules)).                                                                        |
| `conversation`                              | `Conversation/*` et `ConversationMessage/*` : une chronologie indépendante du canal qui agrège courrier et chat, avec `archive`, `link`/`unlink`, `merge`, `markRead`.                                        |
| `threads`                                   | Les opérations au niveau du fil au-delà du `Thread/get` de la RFC 8621.                                                                                                                                       |
| `admin`                                     | La surface d'administration en JMAP (ADR-075) : `Tenant/get`/`set`, `AuditLog/query`, `AccountStorage/get`, les `Principal/*` d'administration, `JournalingRule/get`/`set`. Exige une session administrateur. |
| `resource-limits`                           | Les limites du serveur annoncées comme données, pour que les clients se bornent avant de rencontrer les erreurs.                                                                                              |
| `idempotency`                               | Déduplication persistante par `clientId` sur les créations rejouables (réessais clients sûrs, hors-ligne d'abord).                                                                                            |
| `provenance`                                | La provenance du binaire sur la session (`{sourceUrl, license, commit}` de la version qui tourne).                                                                                                            |
| `streaming`                                 | `Push/catchup` (réconciliation de reconnexion en un appel) et `Core/queryStream` (parcours par curseur), tous deux avec objets `fetchProperties` en ligne optionnels.                                         |
| `push-patches`                              | Des trames **PushPatch** émises par le serveur sur le WebSocket : le push transporte les données changées elles-mêmes, ce qui supprime les allers-retours `Foo/changes` + `Foo/get` après notification.       |
| `projection`                                | Marqueur de conformité : le modèle unifié de sélection de propriétés (vocabulaire fermé par type, joker `*`) tient sur tous les types d'objets v2 : voir [le canon de projection](#le-canon-de-projection).   |
| `e2e`                                       | `{"serverSidePreviewSuppressed": true}` : le contrat du serveur de ne jamais générer d'aperçus depuis des corps chiffrés de bout en bout.                                                                     |
| `calendar`, `contacts`                      | Les homologues v2 des surfaces agenda et contacts (double capacité avec les standard).                                                                                                                        |

Pour chaque capacité, le contrat de fil qui fait foi est ce que la session annonce, plus les comportements des méthodes. Les règles de rigueur ([page courrier](./jmap-mail)) s'appliquent aux méthodes v2 à l'identique : arguments et propriétés inconnus sont des erreurs, jamais ignorés en silence.

## Ce que les objets de capacité annoncent

Les objets de capacité de la session se décrivent eux-mêmes : les clients construisent leur interface à partir d'eux, au lieu de coder en dur une connaissance du serveur. Les entrées porteuses telles que servies en v0.30.0 :

* **`v2:rules`** porte tout le vocabulaire de l'éditeur de règles : `supportedFields` (18 champs de condition) et `supportedActions` (15 types d'action), un client construit son éditeur depuis ces listes, plus les bornes appliquées (`maxRulesPerAccount: 1000`, `maxActionsPerRule: 20`, `maxConditionDepth: 20`, `maxRuleNameLength: 256`, `perRuleExecutionTimeoutMs`, `maxApplyEmailsPerCall`).
* **`v2:admin`** n'apparaît dans `accountCapabilities` que pour les comptes au rôle administrateur : son absence est ce qui dit au client de ne pas afficher de surface d'administration. Elle annonce `supportedPrincipalRoles`, les faits de rétention (`softDeleteRetentionDays: 30`, `auditLogRetentionDays: 2557`, soit sept ans) et `mfaRequiredForSensitiveOperations: true` : les changements de rôle et les mutations de tenant par un compte qui a enrôlé une passkey exigent une session authentifiée par passkey.
* **`urn:ietf:params:jmap:auditlog`** (réservée aux administrateurs comme `v2:admin`) annonce le journal d'audit inviolable : `{hashAlgorithm: "sha-256", verificationSupported: true, retentionDays: 2557}`. `AuditEvent/verify` recalcule la chaîne de hachage et signale la première entrée altérée ou manquante.
* **`v2:mail`** annonce les bornes d'envoi appliquées : `maxSizeEmail` (50 Mio, aussi pré-annoncée par l'extension SMTP `SIZE`), `maxScheduleDelay` (un an sur le `sendAt` d'`Email/send`) et `emailQuerySortOptions`, le vocabulaire de tri exact qu'accepte `Email/query`. L'objet Email v2 porte aussi `expiresAt` (mise à la corbeille automatique à l'instant choisi), et `Email/send` accepte `requestMdn` (accusés de lecture RFC 8098) et `tlsRequired` (REQUIRETLS RFC 8689, y compris pour les envois programmés).
* **`v2:streaming`** annonce `replayWindowSeconds` (l'horizon réel de rétention du journal de changements, 90 jours : un `Push/catchup` depuis n'importe quel état dans la fenêtre rejoue exactement ce qui a été manqué), `maxQueryStreamChunk: 1000` et la cadence de vérification des abonnements push.
* **`v2:push-patches`** annonce `maxPatchSize` (1 Mio), `maxBatchEntries: 100`, `maxPatchesPerSecond: 10`, `supportedTransports: ["websocket"]` et `maxConcurrentStreams: 0` (pas de flux de requête poussés par le serveur : le `Core/queryStream` cadencé par le client est le mode de flux). Un client s'abonne par connexion WebSocket avec `EnablePushPatches {types, properties}` ; les entrées `updated` portent alors **exactement les propriétés changées**, et les cas dégradés retombent sur un simple `StateChange` (réconciliation par `Push/catchup`).
* **`v2:resource-limits`** n'annonce plus que des limites qu'un mécanisme applique réellement, depuis les mêmes constantes que les gestionnaires : dont `maxTotalRequestsPerMinute`, injectée depuis la configuration effective de limitation de débit de l'opérateur (absente quand aucun limiteur ne tourne). `maxConcurrentRequests` vit sur la capacité core (RFC 8620) et s'applique par compte sur les deux transports JMAP.

Les fils et les conversations ont gagné des surfaces d'organisation de premier rang dans le même cycle : `muted` par fil (hérité par les messages nouvellement ingérés) et `pinned`, `Thread/split`, `Thread/merge`, et `/queryChanges` sur chaque type v2 interrogeable.

## Le canon de projection

Chaque `/get` valide son argument `properties` contre un **vocabulaire fermé, propre au type, et avant toute lecture du magasin**. Un nom inconnu revient en `invalidArguments` qui nomme les fautifs ; `id` est toujours accepté (la RFC 8621 le rend implicite) et `*` sélectionne toutes les propriétés de premier niveau.

C'est une erreur, et non un haussement d'épaules, pour une raison précise. Avec l'ancien motif « sérialiser puis filtrer », une propriété mal orthographiée produisait un **succès** avec la propriété absente : indiscernable d'une propriété que le serveur aurait choisi de ne pas servir. Un client ne pouvait pas distinguer sa faute de frappe d'une limite du serveur, et personne ne le pouvait davantage en lisant les journaux.

Un vocabulaire fermé ne sert que si on peut le lire : le canon est donc publié en forme machine, à côté des types TypeScript générés.

| Émis                 | Ce que c'est                                                                                                                                                                                                                              |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PROJECTABLE_<Type>` | Le vocabulaire exact qu'accepte ce type. Émis **uniquement** pour les types dont le vocabulaire est fermé : un canon partiel serait le mensonge même qu'il remplace. `Email` en a deux, un pour `properties` et un pour `bodyProperties`. |
| `DERIVED_<Type>`     | Les propriétés de fil que le gestionnaire **calcule pour l'appelant** et injecte après sérialisation : le nom, le type TypeScript, et la condition d'émission.                                                                            |
| `<Type>Wire`         | Le type augmenté : la structure plus ses propriétés dérivées.                                                                                                                                                                             |

`DERIVED_<Type>` est la partie qu'un client ne peut pas reconstituer seul. Le `mayEdit` d'un [annuaire de tenant](./jmap-contacts#les-annuaires-de-tenant) est calculé par appelant depuis son rôle de tenant et une expansion de groupes côté serveur ; aucun champ de structure ne le porte, et le type TypeScript seul n'en dit donc rien. Le registre ne liste **que** de vraies injections hors structure : une propriété qui est un champ de structure réécrit par lecteur, comme `myRights`, est déjà typée et en est délibérément absente.

Le canon publié est un contrat **additif seulement**, la même règle que la surface `--json` de la ligne de commande. La capacité `v2:projection` de la session est le marqueur de conformité qui atteste que le modèle vaut pour tous les types.

<Note>
  Stabilité du fil : au sein de la ligne v0.30, les formes v2 évoluent de façon **additive** : nouvelles propriétés optionnelles et nouvelles méthodes, pas de renommage ni de changement de sémantique des champs servis. Les durcissements qui transforment un argument jusque-là ignoré en erreur sont étagés (journalisation d'abord, rejet une fois les clients confirmés propres).
</Note>
