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

# oximail-core

> La crate socle de JMAP Core : session, dispatch des méthodes, capacités, push, gestion des blobs, erreurs et EventBus inter-domaines.

`oximail-core` est la crate socle sur laquelle reposent toutes les autres crates de domaine. Elle implémente **JMAP Core** ([RFC 8620](https://www.rfc-editor.org/rfc/rfc8620)) : la ressource de session que le client utilise pour découvrir le serveur, le répartiteur qui route un lot de méthodes, le modèle de capacités qui les autorise, les types de push, les primitives de blob et d'identifiant, le vocabulaire d'erreurs typées, et l'EventBus qui permet aux crates de domaine de communiquer sans s'importer les unes les autres.

## Overview

Toute requête JMAP authentifiée passe par cette crate. La couche HTTP, dans `oximail-server`, analyse la requête, construit un `RequestContext`, puis le transmet au `MethodRegistry` défini ici. Le registre résout les références arrière, vérifie les capacités et les droits d'accès, puis dispatche chaque appel vers le handler enregistré pour ce nom de méthode. Les crates de domaine (`oximail-mail`, `oximail-calendar`, `oximail-contact`, et les autres) implémentent le trait `JmapMethod` et enregistrent leurs handlers. `oximail-core` ne possède que la mécanique transverse et un petit ensemble de méthodes de niveau core.

Elle se situe au bas du chemin de requête. Elle dépend de `oximail-store` pour les primitives d'identifiant, de blob et d'état, et ne dépend de rien au-dessus d'elle. La crate mail n'importe jamais la crate calendar. Les deux dépendent de `oximail-core` et communiquent par son EventBus.

## Features

* **Ressource de session** (RFC 8620 §2) : le document de découverte unique servi sur `/.well-known/jmap`, qui liste les capacités, les comptes, les comptes primaires, et les URL d'API, d'upload, de download et d'EventSource.
* **Dispatch des méthodes** (RFC 8620 §3.5) : un registre de handlers `JmapMethod` indexés par nom de méthode, qui exécute dans l'ordre chaque appel d'une requête groupée.
* **Résolution des références arrière** (RFC 8620 §3.7) : références de création (`#clientId`) et références de résultat (`{ "resultOf", "name", "path" }`, y compris les clés préfixées par `#` et le pointeur JSON avec le caractère générique `*`).
* **Modèle de capacités** (RFC 8620 §2, ADR-028) : capacités au niveau du serveur, capacités dynamiques par compte, et les surfaces d'URN v1 et v2 annoncées côte à côte.
* **Vocabulaire d'erreurs typées** (RFC 8620 §3.6) : des énumérations d'erreurs au niveau requête, méthode et set, qui se sérialisent vers le `type` JMAP exact.
* **Machinerie de filtre générique** : un combinateur récursif AND / OR / NOT partagé par toutes les méthodes `Foo/query`.
* **Types de livraison push** (RFC 8620 §7) : abonnements push, la charge utile de notification `StateChange`, et les primitives Web Push (RFC 8291) et VAPID derrière la livraison EventSource et WebSocket.
* **EventBus** (ADR-011) : un canal typé de publication et d'abonnement pour les événements inter-domaines.
* **Méthodes de niveau core** : `Core/echo`, `PushSubscription/*`, ainsi que les types de messages sauvegardés (signets), de liens message-tâche, et les surfaces de streaming v2.

## How it works

### La ressource de session

Le client commence par récupérer la ressource de session. `Session::build` assemble la map `capabilities` du serveur, la map `accounts` (le compte personnel de l'utilisateur plus les comptes partagés), la map `primaryAccounts`, les gabarits d'URL d'API, d'upload, de download et d'EventSource, et une chaîne `state` opaque. Les limites annoncées dans la capacité Core (`maxSizeUpload`, `maxCallsInRequest`, `maxObjectsInGet`, et les autres) sont définies comme des constantes dans cette crate. Les valeurs annoncées dans la session et les valeurs appliquées au dispatch proviennent donc d'une source unique.

`Session::build_for_account` ajoute par-dessus le pipeline ADR-028. Il calcule les capacités par compte à partir du plan de l'organisation et du rôle du compte, puis réduit `primaryAccounts` aux seules capacités que le compte possède réellement. Les comptes partagés sont ajoutés à part par `add_shared_accounts`, chacun indexé `shared:{ownerId}` et ne portant que les capacités des types d'objets qui ont été partagés.

### Dispatch et références arrière

`MethodRegistry::process` parcourt dans l'ordre les appels de méthode d'une requête. Pour chaque appel, il effectue les étapes suivantes :

1. **Résolution des références arrière** dans les arguments. Une chaîne `"#name"` est remplacée par l'identifiant serveur créé plus tôt dans le lot. Un objet avec `resultOf` / `name` / `path` (ou une clé préfixée par `#`) est remplacé par la valeur située à ce pointeur JSON dans le résultat d'un appel précédent. Une référence vers un identifiant d'appel inconnu, ou un chemin qui ne résout pas, renvoie `invalidResultReference` au lieu d'un `null` silencieux.
2. **Validation de `accountId`** : le compte demandé doit correspondre au compte authentifié, à une forme préfixée par `shared:` de ce compte, ou au compte d'authentification. Une non-correspondance renvoie `accountNotFound`.
3. **Vérification de la capacité** : la capacité requise par le handler (ou l'une de ses capacités additionnelles) doit être présente dans le tableau `using` de la requête, sinon `unknownCapability`.
4. **Application des contrôles d'accès ADR-028** : la capacité doit faire partie des capacités du compte (`accountNotSupportedByMethod` sinon), un compte en lecture seule ne peut pas appeler une méthode `/set` (`forbidden`), et la portée d'un mot de passe d'application peut restreindre davantage l'accès (`forbidden`).
5. **Exécution du handler**, dont le résultat est stocké pour que les appels suivants du même lot puissent y faire référence.

Les identifiants créés sont accumulés sur tout le lot et renvoyés dans le `createdIds` de la réponse. Un échec sur un appel produit une réponse d'erreur pour cet appel et le traitement continue avec le suivant. Le lot n'est pas interrompu.

### Capacités : v1 et v2 coexistent

OxiMail annonce deux générations d'URN de capacité dans la même session : l'ensemble standard `urn:ietf:params:jmap:*` (v1, RFC 8620 / RFC 8621 et apparentées) et l'ensemble OxiMail `urn:oximail:params:jmap:v2:*`. Un handler v2 ne remplace pas son équivalent v1. Il enregistre son URN v2 comme capacité *additionnelle* sur le même handler.

<Note>
  Les capacités v1 et v2 ne sont **pas** mutuellement exclusives. Le répartiteur tient une liste de paires v1 / v2 mutuellement exclusives (`V1_V2_MUTUAL_EXCLUSION_PAIRS`), mais sur la ligne actuelle cette liste est **vide** : chaque domaine qui avait une paire (mail, contacts, calendriers) l'a retirée. Une requête qui liste à la fois l'URN v1 et l'URN v2 d'un domaine dans `using` est acceptée, et la forme du message sur le réseau est l'union des propriétés v1 et v2. Le contrôle d'exclusion s'exécute quand même. Une future surface v3 pourrait donc réintroduire une paire sans réimplémenter le chemin de rejet, mais aujourd'hui il parcourt une liste vide.
</Note>

### EventBus : du cross-domaine sans couplage

L'EventBus est un canal de diffusion typé. Une crate émet un `DomainEvent` (par exemple `EmailIngested`, `CalendarEventCreated`, `StateChanged`) et un nombre quelconque d'abonnés dans d'autres crates y réagissent. Chaque variante d'événement transporte toutes les données dont un abonné a besoin. La crate abonnée n'a donc jamais à importer la crate émettrice. C'est ce qui empêche la crate mail et la crate calendar de dépendre l'une de l'autre : un courriel iTIP entrant devient un événement `ITipReceived` que l'abonné calendrier transforme en événement de calendrier.

`StateChanged` est le pont vers le push. Après qu'un handler `/set` a écrit ses changements, il émet `StateChanged` avec le nouvel état, et le worker push le transforme en notification pour les clients abonnés.

### Livraison push

Les abonnements push (RFC 8620 §7.2) permettent à un client d'enregistrer une URL appelée en POST lorsqu'un état change. La charge utile `StateChange` (RFC 8620 §7.1) transporte une map de l'identifiant de compte vers le type modifié et le nouvel état. Elle peut être marquée d'un vecteur de causalité distribuée, pour que les clients multi-appareils appliquent les patchs dans un ordre déterministe. Le chiffrement Web Push (RFC 8291) et les primitives de signature VAPID se trouvent dans cette crate. La boucle de livraison elle-même s'exécute dans le worker push.

### Invariants

* **Ne jamais échouer en silence.** Les identifiants invalides, les capacités inconnues, les non-correspondances de compte et les mauvaises références de résultat renvoient tous une erreur typée. Rien n'est ignoré.
* **Une source unique pour les limites.** Les limites annoncées dans la session et les limites appliquées au dispatch sont les mêmes constantes.
* **Aucune dépendance vers le haut.** `oximail-core` ne dépend que de `oximail-store`. La communication inter-domaines passe par l'EventBus, jamais par un import direct.

## Configuration

`oximail-core` ne lit directement aucun fichier de configuration. Elle consomme des valeurs résolues par le binaire (URL de base, ensemble de capacités, plan de l'organisation, rôle du compte). Les limites de niveau serveur qu'elle annonce sont des constantes de compilation. Les réglages d'administration qui influencent la surface construite par cette crate (l'URL de base publique, le TLS, les plans et les rôles d'organisation) sont documentés dans la [référence de configuration](../operator/configuration).

## Protocol / JMAP surface

L'essentiel de la surface JMAP est implémenté dans les crates de domaine. `oximail-core` possède la mécanique de dispatch et ces méthodes de niveau core :

| Méthode                                                              | Rôle                                                                                    |
| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `Core/echo`                                                          | RFC 8620 §4.1, renvoie ses arguments inchangés. Le test de connectivité canonique.      |
| `PushSubscription/get`, `PushSubscription/set`                       | RFC 8620 §7.2, gestion des abonnements push d'un client.                                |
| `SavedMessage/get`, `/set`, `/query`, `/queryChanges`, `/changes`    | Messages mis en signet (capacité `savedmessages`).                                      |
| `MessageTaskLink/get`, `/set`, `/query`, `/queryChanges`, `/changes` | Associations entre messages et tâches (capacité `tasklinks`).                           |
| `Core/queryStream`                                                   | Streaming JMAP v2, encapsule un `Foo/query` pour diffuser les résultats par blocs.      |
| `Push/catchup`                                                       | Streaming JMAP v2, rejoue les changements d'état qu'un client en reconnexion a manqués. |

Elle fournit aussi `QueryChangesStub`, un handler générique qui renvoie `cannotCalculateChanges` (RFC 8620 §5.6) pour les surfaces `Foo/queryChanges` qui retombent sur une re-requête complète.

Les contrats réutilisables de filtre et de dispatch qu'elle exporte sont le trait `JmapMethod`, le `MethodRegistry`, et les types génériques `Filter` / `FilterOperator`. Voir [JMAP Core](../developer/jmap-core) pour la présentation du protocole côté client.

## Design decisions

* **ADR-011 (EventBus inter-domaines)** : la crate mail n'importe jamais la crate calendar. Elles communiquent par le canal de diffusion `DomainEvent` défini ici.
* **ADR-028 (Capacités de compte dynamiques)** : les capacités par compte sont l'intersection des capacités du serveur, du plan de l'organisation et du rôle du compte, avec des dérogations explicites. Les contrôles d'accès au moment du dispatch les appliquent.
* **ADR-008 (Identifiants UUIDv7)** : la primitive `Id` (réexportée depuis `oximail-store`). Un identifiant mal formé n'est jamais ignoré en silence.
* **ADR-014 (Déduplication des blobs par hash de contenu)** et **ADR-009 (stockage brut plus structuré)** : la primitive `BlobId` réexportée par cette crate est le SHA-256 du contenu.
* **ADR-054 / ADR-056 / ADR-066 (architecture JMAP v2, négociation de capacités, évolution de schéma)** : le modèle de double capacité v1 / v2 et la liste d'exclusion mutuelle (aujourd'hui vide).
* **ADR-072 (Plafonds de volume par collection)** : les variantes d'erreur `tooManyEntries` et les limites de ressources annoncées.

Les ADR vivent dans le dépôt du serveur OxiMail. Voir l'[index des ADR](../reference/adr-index).

## Standards

* [RFC 8620](https://www.rfc-editor.org/rfc/rfc8620), JMAP Core (session, dispatch, références arrière, capacités, push, erreurs). La référence principale pour cette crate.
* [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750), authentification par jeton Bearer (le jeton EventSource en paramètre d'URL, §2.3).
* [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901), pointeur JSON, utilisé pour résoudre les chemins des références de résultat.
* [RFC 8291](https://www.rfc-editor.org/rfc/rfc8291), chiffrement des messages pour Web Push.
* [RFC 8292](https://www.rfc-editor.org/rfc/rfc8292), VAPID, pour l'authentification du push.
* [RFC 8887](https://www.rfc-editor.org/rfc/rfc8887), JMAP sur WebSocket (capacité `websocket`).

## Source map & tests

| Fichier (dans la crate)                                                                     | Contenu                                                                                                                                                                                   |
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/session.rs`                                                                            | La ressource `Session`, les limites du serveur en constantes, l'assemblage des comptes partagés. Protégé par `test_session_full_checklist`.                                               |
| `src/dispatch.rs`                                                                           | `MethodRegistry`, le trait `JmapMethod`, la résolution des références arrière, la liste d'exclusion v1 / v2, `Core/echo`, `QueryChangesStub`. Protégé par `test_dispatch_full_checklist`. |
| `src/capabilities.rs`                                                                       | `TenantPlan`, `AccountRole` et `build_account_capabilities` (l'intersection ADR-028).                                                                                                     |
| `src/event_bus.rs`                                                                          | `DomainEvent`, `EventBus`, `EventEmitter`.                                                                                                                                                |
| `src/jmap_filter.rs`                                                                        | Le combinateur générique `Filter` / `FilterOperator` et la validation structurelle.                                                                                                       |
| `src/push.rs`, `src/push_store.rs`, `src/push_worker.rs`, `src/web_push.rs`, `src/vapid.rs` | Abonnements push, charge utile `StateChange`, Web Push et VAPID.                                                                                                                          |
| `src/error.rs`                                                                              | `RequestError`, `MethodError`, `SetError`, le vocabulaire d'erreurs JMAP typées.                                                                                                          |
| `src/types.rs`                                                                              | `Account`, plus les réexports `Id`, `BlobId`, `State` depuis `oximail-store`.                                                                                                             |
| `src/methods/`                                                                              | Les handlers de niveau core (`SavedMessage/*`, `MessageTaskLink/*`, `Core/queryStream`, `Push/catchup`, `PushSubscription/*`).                                                            |
| `tests/concurrency.rs`                                                                      | Couverture d'intégration du dispatch concurrent.                                                                                                                                          |

Les modules de dispatch et de session portent chacun un test unique de type liste de contrôle, qui exerce toutes les responsabilités du module en un seul flux. Modifier le comportement du dispatch ou de la session implique de mettre à jour ce test.

## Status

Implémenté et en production en **v0.30.0** : la ressource de session, toute la mécanique de dispatch et de références arrière, le modèle de capacités ADR-028, le vocabulaire d'erreurs typées, le combinateur de filtre générique, l'EventBus, les abonnements push et la charge utile `StateChange`, et toutes les méthodes de niveau core listées ci-dessus.

Les méthodes de streaming v2 (`Core/queryStream`, `Push/catchup`) et les URN de capacité v2 sont annoncées et câblées sur la ligne actuelle. L'infrastructure d'exclusion mutuelle v1 / v2 existe mais reste inerte : la liste de paires est vide, donc aucune requête n'est rejetée pour avoir associé des capacités v1 et v2.
