Skip to main content
OxiMail évolue comme une ligne continue : la version actuelle est la v0.30.0, et la ligne reçoit des lots fonctionnels et de durcissement en continu. Une mise à jour est un remplacement de binaire : les migrations de schéma s’appliquent d’elles-mêmes au démarrage, transactionnellement, sans étape manuelle (exploitation). Le journal complet, changement par changement, est tenu dans le dépôt du serveur et accompagnera son ouverture publique ; cette page résume ce qui a atterri sur la ligne actuelle, du plus récent au plus ancien.

Ligne actuelle : v0.30 (2026)

Début octobre
  • La fédération entre organisations, servie de bout en bout (ADR-124 Am. 8 et 9) : un appairage enregistre désormais comment joindre le pair. oximail federation pair prend son point d’accès (--endpoint), ses épingles TLS (--tls-pin) et, pour un tunnel que vous avez posé vous-même, une adresse privée littérale (--address), et n’épingle qu’une fois confirmée l’empreinte de transport que l’autre administrateur a lue à voix haute (--transport-fingerprint). Le point d’accès et les épingles de ce nœud se déclarent dans une nouvelle section [federation], dont la porte est fermée par défaut. Chaque requête fédérée est signée selon le profil RFC 9421 du brouillon de fédération : elle nomme les deux realms, porte son nonce et le certificat de la clé de travail, et se vérifie sur ses champs d’en-tête avant que son contenu soit lu. Un pair se trouve par son propre enregistrement _jmap-federation._tcp, jamais par le _jmap._tcp des clients, et seulement si cet enregistrement est signé par DNSSEC ; l’assistant le publie une fois la fédération activée (fédération).
  • Ce qu’une organisation appairée peut lire (ADR-168) : chercher un créneau avec quelqu’un d’une organisation appairée lit ses heures occupées sur son propre serveur. Event/proposeTimes interroge le pair qui sert le domaine du participant et n’utilise la réponse qu’une fois sa signature vérifiée ; un pair muet pendant quatre secondes place le participant dans unreachableEmails, jamais en « libre ». Le serveur qui répond donne des plages occupées fusionnées, jamais un événement, et enregistre une divulgation par personne avant de répondre. L’opérateur peut partager un dossier ou un fichier du Drive, en lecture, avec une organisation appairée (oximail federation shares add, list, remove, simulation par défaut) ; la racine d’un Drive est refusée, si bien qu’un partage n’ouvre jamais un Drive entier. Le pair le lit par la porte de fédération, borné au sous-arbre partagé, le contenu en blocs signés de 1 Mio, sous un budget par classe et par appairage ; un partage retiré est journalisé et poussé au lecteur. Côté lecteur, un compte miroir (oximail federation mirror add) montre ces dossiers dans son Drive : la copie vit dans la mémoire du serveur seulement, jamais dans la base ni sur le disque de ce nœud, n’a jamais plus de mirror_staleness_secs (120 s), refuse toute écriture par mirror-read-only, et montre un fichier d’état nommé plutôt qu’un dossier vide tant que le propriétaire est injoignable. Un fichier miroir se télécharge bloc par bloc chez le propriétaire, derrière un cache en mémoire qui ne touche jamais le disque (content_cache_secs, content_cache_mib, content_cache_file_mib) (fédération, partage).
  • Une identité de fédération qui survit à une restauration ou à une clé papier perdue (ADR-127 Am. 5 à 7) : un pair qui réémet une époque sous une autre clé est refusé ; une nouvelle clé de travail prend une époque supérieure à la fois à la précédente et à l’horloge, si bien qu’une boîte restaurée depuis une base plus ancienne ne réutilise jamais une époque que ses pairs ont vue ; la clé papier d’une racine retirée n’ouvre plus rien, et oximail federation roots retire efface son séquestre. Une identité dont la chaîne ne se vérifie plus se recertifie avec la clé papier d’une racine sœur enrôlée (oximail federation working-key repair), et oximail federation roots delete en abandonne une volontairement, après quoi chaque pair refait l’appairage. La juridiction de fédération d’un locataire est son pays d’établissement déclaré : oximail federation identity la lit à voix haute à côté des empreintes, un appairage enregistre ce qui a été déclaré à chaque pair, et doctor signale un pays changé depuis (fédération).
  • La recherche plein texte dit vrai (ADR-167) : un index qu’on ne peut pas ouvrir n’est plus un repli silencieux. doctor le nomme, oximail_search_index_state le publie, et Email/query déclare une réponse donnée sans lui (fullTextIndexUnavailable). Après une mise à jour, l’index se reconstruit seul sur sa propre tâche, servi en entier pendant ce temps, et oximail reindex le reconstruit serveur arrêté, environ huit fois plus vite qu’avant. L’index tient désormais à part le sujet, les adresses From, To et Cc et le corps : body cherche dans le corps seul, text dans les cinq, et un mot du sujet ou de l’expéditeur pèse davantage. Au-delà de 10 000 messages trouvés, un total est un vrai compte et non le plafond, une liste triée par date contient chaque résultat, et un mot exclu ou un OR s’évalue exactement. Un mot pris dans un filtre AND, OR ou NOT se cherche dans l’index et non plus dans l’aperçu de 256 caractères, et SEARCH BODY et SEARCH TEXT en IMAP passent par le même index : Thunderbird, Apple Mail et Outlook trouvent un mot n’importe où dans le corps. Une recherche de ponctuation ne renvoie plus toute la boîte, le surlignage marque le mot trouvé (reunion marque Réunion), et une note détruite quitte l’index même quand l’index était fermé à ce moment (courrier, exploitation).
  • Un MX de secours jugé sur le vrai client (ADR-166) : le courrier relayé par un MX de secours est désormais jugé sur l’adresse du client que le secours a servi, avec SPF, DMARC et DNSBL appliqués comme pour un client direct. Le primaire reconnaît ses secours d’après les enregistrements MX de ses propres domaines, sans réglage, et n’en traite jamais un comme un pair de confiance ; le secours écrit sa propre ligne Received avant de relayer, comme l’exige la RFC 5321, si bien que le primaire peut lire cette adresse. L’assistant du MX de secours ne génère ni ne publie plus de clé DKIM pour le domaine (avec un jeton Cloudflare, il pouvait remplacer la clé du primaire et casser ses signatures), ni d’enregistrements SRV clients, et doctor ne demande plus à un secours le SPF et le DKIM du domaine. La ligne de journal d’un message accepté nomme le client sur lequel il a été jugé et les verdicts SPF, DKIM et DMARC (courrier et SMTP, anti-spam).
  • Un DNS auquel la boîte peut se fier et qu’elle peut republier (ADR-165) : le serveur résout par le résolveur validant local et ne croit une réponse DNSSEC qu’une fois ce résolveur prouvé par une sonde (oximail resolver check). Un déploiement refuse une boîte non prouvée, et les livraisons DANE attendent plutôt que de partir sans authentification (oximail dane suspend lève cette règle pour une durée bornée). La configuration du résolveur ne nie plus pendant une demi-heure un enregistrement tout juste publié (oximail resolver ensure --apply réécrit une ancienne configuration), et l’assistant vide le cache local après publication. oximail setup dns relancé lit d’abord la zone et ne double jamais un enregistrement (un second TXT _mta-sts laissait le domaine sans politique MTA-STS), tourne sans terminal (--replace-spf, --replace-dmarc, --public-ipv4), et trouve la zone Cloudflare d’un sous-domaine délégué. doctor contrôle chaque domaine de courrier par rapport au vrai nom d’hôte (oximail check-dns --hostname), et une DNSBL qui refuse ou ne sait pas répondre n’est plus lue comme « non listé » (installation, exploitation).
  • Connexion, bannissements et appareils (ADR-031 Am. 1) : un bureau derrière une seule adresse n’est plus banni à cause d’un invité ou d’un téléphone resté sur un ancien mot de passe. fail2ban bannit une adresse quand cinq identifiants différents y échouent en quinze minutes, comptés à part pour le courrier, le web et la fédération ; un identifiant qui s’est connecté depuis là dans les 30 derniers jours, ou le même mauvais mot de passe retenté, ne compte pas. L’IPv6 se compte par /64, les bannissements répétés montent d’une heure à un jour, un jeton valide fonctionne encore depuis une adresse bannie, et oximail ban list montre la classe et la raison. Un nouveau mot de passe d’application se vérifie en une lecture et un HMAC au lieu de cinq hachages Argon2, et les anciens continuent de fonctionner. Un client de messagerie sur mot de passe d’application voit de nouveau les boîtes partagées avec son utilisateur, et chaque ligne de connexion IMAP, SMTP, CalDAV et CardDAV nomme l’identifiant et l’adresse du client, si bien qu’un administrateur distingue un appareil d’un autre. Le profil Apple, l’autoconfig Mozilla et l’Autodiscover Microsoft échappent toute valeur qu’ils écrivent au lieu de recopier la requête dans leur XML (anti-spam, protocoles historiques).
  • Une clé privée gardée sur le serveur, jamais lisible par lui : le titulaire d’un compte personnel peut garder une copie de sa clé privée OpenPGP ou S/MIME déjà protégée par sa phrase de passe (PrivateKeyEnvelope/get, PrivateKeyEnvelope/set, PrivateKeyEnvelope/changes). Le serveur en refuse une en clair, ne la déchiffre jamais, et ne la révèle, ne la remplace ou ne la supprime qu’après une connexion récente de la session. Une preuve manquante répond stepUpRequired, si bien qu’un client distingue un refus qui se corrige d’un refus définitif. Un compte sans passkey reprouve son mot de passe sur la session ouverte (POST /auth/password/reauth) au lieu de se reconnecter, et un mot de passe ne vaut jamais preuve de passkey (courrier).
  • Chat et appels : un appel laisse sa trace dans son salon, un message d’appel par session (audio ou vidéo, durée, qui a répondu, manqué, refusé), non lu seulement pour qui l’a manqué. ChatRoom.lastMessageCall permet à une liste de salons d’écrire « appel manqué » sans charger de message, et l’appelant lit qui n’a pas répondu (MeetSession.missed). Une personne peut trouver les salons où elle peut demander à entrer (ChatRoomDirectory/get et ChatRoomDirectory/query, annoncés par roomDirectory dans la capacité chat), avec leur nom, leur description et leur nombre de membres seulement. ChatMessage/query lit une ancre comme le dit la RFC 8620 (anchorNotFound), un fil lu depuis une ancre ne liste que ce fil, et une recherche répond le total demandé (chat, Meet).
  • Des connexions qui se terminent proprement : à un redémarrage, les écoutes SMTP, IMAP et HTTPS cessent d’accepter, une session inactive reçoit 421 4.3.2 ou * BYE [UNAVAILABLE], une session en pleine transaction la termine, et l’arrêt nomme les tâches qu’il attendait encore. La limite SMTP de cinq minutes devient une limite d’inactivité, réarmée à chaque commande (421 4.4.2 pour un pair muet, une heure au plus par session). Chaque connexion acceptée désactive Nagle, ce qui retire 40 ms à chaque commande IMAP sur une boîte sans proxy. Le serveur sonde chaque WebSocket et ferme celle qui ne répond plus, si bien qu’un téléphone en veille retrouve son Web Push, et un patch de push dit null quand une propriété est vidée (exploitation).
  • Observabilité : où passe le temps d’une écriture (attente du verrou SQLite, verrou tenu, COMMIT, écritures hors transaction), la latence IMAP par commande et par résultat, le délai SMTP après les données par écoute et par classe de réponse, la barrière de durabilité, les push retenus, les réponses DNSBL par nature et la confiance dans le résolveur sont des séries Prometheus aux libellés fermés, jamais une adresse ni un compte. Un administrateur voit, pour chaque appareil d’un compte, s’il a un abonnement push vivant et quand son service de push a accepté une notification pour la dernière fois, jamais l’URL du point d’accès (exploitation).
  • Et aussi : un nom que Windows réserve (CON, NUL, aux.txt et autres) est refusé dans le Drive à la création et au renommage, puisque le partage SMB le servait et que l’Explorateur ne pouvait ensuite ni l’ouvrir ni le supprimer ; l’autocomplétion d’adresse cherche d’abord dans le pays du locataire ; la fin d’une installation dit comment un appareil reçoit ses réglages.
Derniers jours de septembre
  • Des sauvegardes pour une boîte sans hyperviseur : oximail backup --to écrit un dépôt incrémental chiffré par age, que la boîte peut écrire mais jamais lire, et qui se restaure avec le seul outil age. Dans un bucket s3://, il exige l’Object Lock et refuse une clé qui pourrait supprimer ou lire (un bucket en mode GOVERNANCE reçoit un avertissement). Une exécution peut être bornée en débit et en durée, envoie les gros objets par parties et date ses instantanés à l’horloge du dépôt, et une vérification complète laisse un reçu daté que doctor lit. oximail account import --from-backup ramène un compte à côté de ce qu’il contient, notes comprises, et oximail chat restore-room --from-backup accepte un dépôt. Un plan de sauvegarde que rien n’a jamais confirmé est un avertissement, plus un vert. Après une restauration, les clés DKIM déléguées sont remplacées, et les clients IMAP se resynchronisent au lieu d’attacher un UID réutilisé à un autre message (exploitation).
  • Un effacement qu’une restauration ne défait pas : chaque effacement achevé est inscrit dans un registre qui voyage avec les sauvegardes ; une personne qu’une restauration a ramenée est gelée, et oximail erasure replay l’efface de nouveau. Les copies de son courrier que conserve la conservation légale d’un collègue restent telles quelles et sont caviardées quand elle prend fin, et la fermeture d’un compte retire sa fiche de l’annuaire des personnes et de chaque carnet d’adresses (conformité).
  • Le filtrage, c’est Rules seulement, appliqué à la remise : les filtres de courrier sont des Rules typées (déplacement par boîte ou par rôle, mots-clés, en-têtes nommés, comparateurs). Les méthodes SieveScript, la capacité JMAP Sieve et ManageSieve sont retirées, et les scripts actifs stockés sont importés en Rules au démarrage. Les règles répondent automatiquement et la réponse d’absence est envoyée ; une règle en échec laisse le message dans la boîte de réception ; une règle dont la cible est détruite devient inerte ; les notifications s’arrêtent à trente par heure et par compte. Rule/test teste une règle non enregistrée ou l’ensemble, Rule/apply nomme ses messages et figure dans la piste d’audit, et sa borne par appel est [rules] max_apply_emails_per_call (règles).
  • Le transfert gardé comme un identifiant, et vérifié (ADR-162, ADR-163) : poser un transfert exige une preuve de passkey récente quand le compte a une passkey, et se refuse avec un mot de passe d’application. Un mot de passe réinitialisé par un administrateur désactive toutes les règles de transfert et annule les copies encore en file, et chaque transfert posé ou retiré figure dans la piste d’audit sous une empreinte de l’adresse. Avec [rules] forward_policy = "verified", une règle ne transfère qu’à une adresse dont le titulaire l’a confirmée : le compte demande par ForwardConfirmation/set, le titulaire reçoit un code valable sept jours, et ForwardConfirmation/confirm ouvre la destination. Le message du code nomme le responsable du traitement, les pays où l’adresse est traitée, à qui écrire et comment rendre le code ; une demande restée sans réponse est effacée à l’expiration de son code, et l’opérateur peut retirer et bloquer une adresse à la demande de son titulaire (règles).
  • Qui est responsable, déclaré (ADR-164) : un locataire déclare sa nature, sa raison sociale, son adresse, son pays d’établissement et des contacts typés (oximail tenant identity set, legalIdentity sur Tenant/set) ; l’instance déclare qui l’exploite et où les données sont traitées (oximail admin instance identity set, à la console seulement). Un champ laissé vide hérite de celui de l’instance, et doctor legal_identity nomme les champs manquants, jamais leurs valeurs. Les textes envoyés aux tiers nomment ce responsable, et un opérateur peut installer une variante nationale qui ajoute, après le texte mondial, les paragraphes citant sa loi ; aucune n’est livrée tant qu’un praticien n’en a pas validé une. Les comptes reçoivent la langue par défaut du locataire (multi-tenant, conformité).
  • Les clients JMAP standard reçoivent ce que promettent les RFC : une création répond les propriétés que le serveur a posées (valeurs par défaut, valeurs calculées, adresses normalisées) sur tous les types, comme le demande la RFC 8620 §5.3, et un objet entier reçu de Foo/get est un patch de mise à jour valide. Email/get sert les en-têtes dans l’ordre du message et header:<Name> sous les sept formes de la RFC 8621, et textBody et htmlBody suivent le §4.1.4. EmailSubmission/set envoie un brouillon créé par un client standard et répond des refus typés ; Thread/changes signale les conversations créées et détruites. Une requête peut ranger un message dans une boîte créée plus tôt dans la même requête (#creationId) ou modifier un enregistrement par sa référence de création ; alreadyExists nomme l’enregistrement existant (existingId) ; une méthode hors de using répond unknownMethod. Chaque capacité propre à OxiMail est servie sous son URN et sous son URL (https://oximail.ch/jmap/<name>), et une collection restée sans changement pendant 90 jours garde son état au lieu de répondre cannotCalculateChanges pour toujours (cœur JMAP, courrier).
  • Conformité IMAP, CalDAV et CardDAV (mesurée avec imaptest et de vrais clients) : \Draft est un drapeau permanent, les clients IMAP4rev1 voient \Recent, LIST suit LIST-EXTENDED et CREATE n’abonne plus. SORT suit les RFC 5256 et 5957, THREAD ne garde plus un parent que les propres References d’un message contredisent, APPEND garde sa date, les changements de drapeaux et les suppressions d’une autre session atteignent NOOP, et ENVELOPE écrit les groupes d’adresses comme l’en-tête les écrivait. Le sujet, l’adresse ou le nom de fichier d’un expéditeur ne peuvent plus écrire une réponse IMAP à eux, une ligne de commande a un plafond, et IMAP tient au plus 20 connexions par compte et par adresse. Les clients d’agenda peuvent renommer et recolorer un agenda ou une liste de tâches, les requêtes CalDAV et CardDAV respectent le composant, la collation, la limite, les en-têtes conditionnels et la profondeur, un événement récurrent sans fin reste au-delà de sa première année, et une suppression atteint les autres appareils (protocoles historiques).
  • SMTP, DMARC, ARC et MTA-STS, refermés : DATA ne se termine qu’à <CRLF>.<CRLF>, si bien qu’une transaction porte un seul message ; la porte entrante répond comme le dit la RFC 5321, et postmaster@ et abuse@ atteignent le locataire sur chacun de ses domaines. Un From: falsifié ne passe plus DMARC et un message sans auteur unique est refusé ; les signatures c=simple se vérifient, un domaine à exactement dix termes SPF passe, et une signature dont le l= laisse une partie du corps non signée vaut dkim=policy. L’exception ARC à DMARC n’honore que les scelleurs nommés dans [spam] arc_trusted_sealers, vide par défaut, si bien qu’un message ne peut plus se cautionner lui-même avec une chaîne qu’il a scellée. MTA-STS ne fait correspondre un motif générique qu’à un label entier. Une recherche DANE en échec ne peut plus revenir en « pas de TLSA » par la liste de recherche de l’hôte, et la mise à jour de la bibliothèque DNS permet de nouveau la validation des domaines sous un TLD signé en RSASHA1-NSEC3-SHA1. Les ports de courrier refusent un client TLS qui demande un autre protocole et coupent une session après trois commandes inconnues avant authentification, et chaque écoute borne une poignée de main TLS à 10 secondes. Derrière un proxy inverse local, OxiMail peut servir le HTTP sur une socket Unix que seul le groupe du proxy peut ouvrir ([server] bind = "unix:<path>"). Un message envoyé depuis un client de messagerie ne révèle plus à ses destinataires le nom et l’adresse du poste de travail (courrier et SMTP, authentification du courrier).
  • Des comptes sous attaque : une passkey ne peut plus s’enrôler depuis un mot de passe d’application ou un mot de passe seul, les actes sensibles exigent une preuve de passkey des dix dernières minutes, et un mot de passe réinitialisé par un administrateur ferme tous les mots de passe d’application et toutes les passkeys du compte. Un compte attaqué est ralenti sur tous les protocoles, jamais verrouillé, et un client CalDAV ou CardDAV ne coûte plus six hachages de mot de passe par requête. Un appel composé dans un autre (Core/queryStream, les patchs de push) passe la garde des comptes partagés et laisse sa ligne dans le journal des accès. Les secrets gardés sous la clé du locataire sont scellés, liés à leur champ et à leur ligne. L’annuaire de clés ne sert jamais une clé OpenPGP révoquée ou expirée, et OpenPGP passe à rPGP 0.20, qui borne la décompression d’un message importé. Un client de chat ne peut plus forger la réaction d’un autre membre ni cacher qu’un message a été modifié (conformité).
  • L’envoi à grande échelle (ADR-157) : le courrier sortant attend dans trois classes, si bien que le message d’une personne part avant une campagne en cours, même vers le même hôte, et le répartiteur fait tourner les 16 créneaux de remise prévus au lieu de 4. Un compte peut porter des flux d’envoi (oximail account flow create), chacun pouvant partir de sa propre adresse ([[smtp.egress_pool]], oximail account flow set-pool), jugés par un objet d’admission unique qui tourne pour l’instant en observation (doctor compte les jours où il est d’accord). Le relais signe le courrier d’un client avec le propre domaine du client (oximail account signing-domain add, deux CNAME à publier), et répond aux exigences des grands fournisseurs envers les expéditeurs : les plaintes comptent contre le flux qui a envoyé, un flux se met en pause à 0,3 % de plaintes, le courrier de masse exige un désabonnement en un clic signé et un domaine doté de DMARC, et le courrier d’origine porte un Feedback-ID. La preuve d’un domaine d’envoi appartient au propre domaine du locataire, et le courrier sortant part en IPv4 dès que la destination en a une. Un 250 après les données signifie désormais que le message survit à une coupure de courant : une barrière de durabilité s’exécute avant lui sur les portes nommées dans [storage] durable_doors (relais sortant, envoi de masse).
  • Les pièces jointes dangereuses, observées d’abord (ADR-155) : exécutables, images disque, extensions listées, macros et archives illisibles peuvent être refusés avant le 250, sur le port 25, dans la file du MX de secours et à la soumission, et la même politique couvre le courrier du client web, les pièces jointes du chat et les téléversements ; les imports et l’APPEND IMAP sont tracés, jamais refusés. Les anciens documents Office, les messages Outlook et winmail.dat sont lus, et un fichier du Drive dont le contenu est un exécutable est servi en application/octet-stream. Une nouvelle boîte observe et compte avant qu’aucune règle ne soit armée (politique des pièces jointes).
  • Courrier, notifications et octrois : un message chiffré porte le mot-clé $encrypted (oximail email backfill-encrypted marque ceux déjà stockés) et ne déclare plus un statut S/MIME « unknown ». Un aperçu est le texte du message, sans les annotations de liens ni les marqueurs d’images qu’un convertisseur y avait écrits (oximail email reindex-bump --link-preview recalcule ceux qui sont stockés), et SenderBrand/get donne le logo vérifié d’une adresse sans aucune requête sortante. Une notification de nouveau message dit si l’expéditeur est un collègue, un contact ou un inconnu, si le message est un envoi de masse et s’il répond à l’un de vos messages ; « Plus tard » sur une alerte la fait sonner de nouveau, une fois, à l’heure choisie ; un appel entrant peut être refusé et sonne 45 secondes sur un appareil dont l’appli est fermée. Un octroi de compte (oximail account grant) ouvre désormais chaque objet de sa collection à son niveau, y compris ceux créés après lui, et ne partage ni n’administre jamais (courrier, CLI).
Fin septembre
  • Les signatures S/MIME reçues sont vérifiées (ADR-156) : la RFC 9219 telle qu’écrite, sous urn:ietf:params:jmap:smimeverify, avec smimeStatus, smimeStatusAtDelivery, smimeErrors, smimeVerifiedAt et trois filtres. Les deux verdicts sont stockés et réévalués à leurs échéances (fin du chemin de certificats, prochaine CRL), si bien qu’une expiration ou une révocation atteint Email/changes. La révocation se contrôle par CRL, jamais par OCSP. Le magasin de confiance est celui des racines « email » de Mozilla plus les intermédiaires du CCADB, et le détail du signataire est smimeSigner sous v2:mail. Le serveur ne déchiffre jamais : un message chiffré se vérifie côté client. Le courrier stocké avant la fonction se marque par oximail smime backfill (messages signés, côté opérateur).
  • Les clés OpenPGP trouvées par le serveur : RecipientKey/lookup essaie le WKD du domaine du destinataire, puis le serveur de clés, et distingue « introuvable » de « injoignable » ; PgpKey/publish publie sa propre clé. Annoncé sous recipientkeys et réglable dans [pgp_directory], il suit la règle selon laquelle le navigateur ne parle qu’à sa propre origine (clés OpenPGP).
  • Un DANE qui tient dans la durée : un renouvellement ACME garde la clé TLS, si bien que l’épingle 3 1 1 ne bouge plus tous les 60 à 90 jours. oximail dane rotate change la clé volontairement, en publiant l’épingle suivante avant la bascule. Les enregistrements TLSA peuvent se publier sur votre propre DNS par mise à jour dynamique RFC 2136 (TSIG). Les épingles ne couvrent que les ports SMTP (un MX de secours n’épingle que le port 25), et la clé que sert réellement chaque port épinglé se lit sur le fil, par doctor et par une sonde horaire. Un renouvellement raté garde en service le certificat encore valide au lieu de le remplacer par un auto-signé. Le démarrage refuse un résolveur incapable de prouver une non-existence, et une réponse TLSA invérifiable rend la destination injoignable plutôt que « sans DANE ». Les boîtes qui terminent elles-mêmes le TLS offrent HTTP/2 (TLS et ACME, enregistrements DANE).
  • L’envoi, les défauts qu’une revue du relais a trouvés : un domaine relais ou d’expéditeur d’enveloppe dont la preuve DNS a été suspendue, ou n’a pas été confirmée depuis 72 heures, diffère désormais en 451 4.7.1 au lieu d’autoriser pour toujours ou de rebondir, et doctor sender_domain_proofs le surveille. Une soumission sans Date: ni Message-ID: en reçoit un du serveur, et un deuxième From: est refusé et jamais signé. Un domaine From: internationalisé est signé par sa clé. Un message qu’une règle fait suivre n’est signé par la clé d’un domaine hébergé que si son auteur a été authentifié. La vérification DKIM entrante est bornée (8 signatures, 30 secondes). Un rapport de rebond porte de nouveau le Message-ID d’origine, si bien que les outils de traitement des rebonds peuvent le rattacher. Un rapport de remise qui ne vient pas de l’expéditeur nul ne change rien (courrier et SMTP, relais sortant). ADR-157 dit où cela mène : boîtes et relais sur un seul serveur, la décision d’envoi prise à l’admission. C’est un design, et seuls ses premiers lots de défauts sont servis.
  • Le départ d’un collaborateur, depuis la surface d’administration : suspendre, envoyer son courrier à un collègue jusqu’à une date, et donner à ce collègue la lecture de la boîte (Principal/setAdmin {forwardTo, forwardUntil, mailboxGrantTo, grantUntil}). Poser le mot de passe de quelqu’un derrière un second facteur strict (Principal/setPasswordAdmin), voir et couper une session d’appareil ou un mot de passe d’application d’un autre compte, et suivre une demande d’effacement jusqu’à son enregistrement d’achèvement (ErasureJob/queryAdmin). Chaque acte est journalisé sous son sujet (API d’administration).
  • La santé de la boîte, lisible en JMAP (ADR-154) : un minuteur enregistre la ronde doctor toutes les 15 minutes, et HealthReport/query et HealthReport/get la servent. Sur une boîte à un seul locataire, l’administrateur du locataire la lit ; sur une boîte à plusieurs locataires, seul un opérateur d’instance la lit, un rôle posé à la console seulement (oximail admin instance grant) sur un administrateur doté d’un second facteur. doctor --json donne à chaque contrôle sa famille, ses faits datés et, sur un avertissement, le verbe à taper (l’opérateur d’instance, la ronde doctor).
  • Organisations, comptes et identifiants : un administrateur déclare quels groupes sont les entités de l’organisation, les employeurs qu’une fiche de profil peut citer (isEntity, oximail admin organization create, set-entity, list, set). Un compte de service se déclare, il ne se déduit jamais, et ses octrois ne portent pas d’échéance. Un mot de passe d’application peut être en lecture seule : JMAP seulement, toute écriture répondant accountReadOnly. Un mot de passe d’application mail peut envoyer par JMAP comme il le faisait déjà par SMTP. GET /health ne compte que l’organisation de l’appelant, et la suppression d’une organisation est refusée tant qu’elle porte un compte ou un blob (multi-tenant).
  • L’assistant d’installation finit le travail : en plein écran sur un terminal capable, il mesure l’hôte, demande comment la boîte sera sauvegardée (un snapshot de l’hyperviseur confirmé par oximail backup mark, ou un oximail backup planifié), mène la cérémonie de la clé de récupération (montrée une fois, retapée, l’exercice enregistré ; oximail key escrow --guided sur une boîte existante), crée le jeton /metrics, surveille l’IP d’envoi sur les listes de blocage et termine par une ronde d’accessibilité. L’escrow porte désormais aussi le secret d’instance, si bien qu’une boîte restaurée sur un nouveau matériel reste la même boîte. La télémétrie communautaire (ADR-153) demande d’abord, sans valeur par défaut, et une boîte sans réponse n’envoie rien ; oximail telemetry enable, disable, status et purge la changent ou l’inspectent (premier démarrage, télémétrie).
  • Configuration : les secrets SRS et TURN ont quitté le TOML pour des fichiers (secret_key_file, turn_shared_secret_file ; oximail config externalize-secrets les déplace). Chaque organisation a ses propres surcharges par /admin/v1/tenants/{id}/config, et oximail config set-override refuse ce que cette route refuse. Le budget sortant se règle par compte. Les six corbeilles (Drive, agenda, contacts, tâches, notes, chat) ont une rétention par organisation (configuration).
  • Les logos de marque des expéditeurs externes (BIMI) : affichés quand le serveur a vérifié le certificat de marque de l’expéditeur, avec brandMarkType qui distingue une marque enregistrée (VMC) d’une marque d’usage (CMC). Un BIMI-Selector signé par DKIM est honoré, et oximail bimi backfill donne leur logo aux messages déjà en boîte (configuration).
  • Chat : le corps d’un message ou d’un salon est content ; encryptedContent n’est plus ni servi ni accepté. La transcription d’un message vocal est calculée une fois par le serveur (ChatMessage/transcribe, [chat] transcript_policy). Les fils se suivent explicitement et une réponse ne notifie que les suiveurs. La préférence de notification d’un salon est appliquée par le serveur, donc aussi au push d’une appli fermée. Les accusés de lecture peuvent se couper, réciproquement. Une écriture ne peut citer qu’un fichier que son auteur peut lire (chat).
  • Courrier et recherche : un mot se trouve par son début, et un tri par pertinence fait passer un mot présent dans le sujet ou l’expéditeur, et le courrier récent, devant les vieilles notifications qui le répètent. Le corps d’un message joint arrive dans la même réponse (viaMessagePartId), Email/set accepte bodyStructure à la création, et un caractère de contrôle dans une adresse ou un sujet est l’erreur du client (invalidProperties), sur le courrier comme sur les contacts. oximail email normalize-received-at répare les dates qu’un ancien import avait stockées au mauvais format (courrier).
  • Agenda : une seule forme de récurrence, l’objet RFC 8984 d’un profil fermé, une règle par événement ou tâche, avec les règles d’exclusion appliquées et les règles mensuelles et annuelles développées comme le dit la RFC 5545. DTEND devient la durée sur toutes les portes et revient inchangé en CalDAV. La disponibilité dit quand elle n’a pas pu tout voir (complete, incompleteFor sur Event/proposeTimes), et la page de réservation n’offre aucun créneau sur une telle personne. Un message iTIP entrant n’est appliqué que par quelqu’un qui a autorité sur l’événement (agenda).
  • Chaque type déclare ce qu’on peut y écrire : une propriété inconnue est refusée par son nom avant le gestionnaire, sur tous les types de la surface (JMAP v2). Chaque écriture enregistre son entrée de journal des changements dans sa propre transaction, si bien qu’un échec annule les deux au lieu de laisser un changement qu’aucun client de synchronisation ne verra jamais.
Mi-septembre
  • Un agenda partagé ne laisse plus fuir ce que la confidentialité cache (ADR-152) : le partage réduisait déjà ce qu’un collègue reçoit d’un événement private et ne servait rien d’un secret, mais la recherche regardait toujours le contenu réel : un filtre sur le titre répondait oui ou non, et une borne de temps révélait les créneaux secret. « Ce que ce lecteur peut voir » se déclare désormais une fois et se rend deux fois, en prédicat SQL pour les surfaces qui rendent des identifiants et en réduction pour celles qui rendent des objets, liés par un test d’accord : un secret n’apparaît dans aucun résultat, aucun total, aucune page ; un private reste visible par le temps ou le calendrier et sort du jeu dès qu’un filtre de contenu est posé ; le propriétaire ne perd rien. La même règle atteint les neuf sites de rapport CalDAV, et le journal des accès porte désormais deux nombres par lecture, combien d’événements ont été servis réduits à leur créneau et combien ont été retirés, absents plutôt qu’à zéro quand rien n’a été jugé.
  • Les quotas de stockage tels que livrés (ADR-151) : deux quotas par personne, courrier 50 Gio et Drive 1 Tio par défaut, aucun pour un compte partagé, une boîte système ou un compte déclaré illimité, et plus de quota de locataire. Le contrôle est l’écriture ; SMTP diffère par 452 4.2.2, JMAP répond overQuota, IMAP NO [OVERQUOTA] ; une garde d’espace disque du serveur ([storage] min_free_percent, 5 % par défaut) diffère le courrier entrant et refuse les ajouts avant que le disque soit plein (stockage). La route de téléversement du locataire, celle des pièces jointes du chat et des avatars de salon, passe enfin par la même admission.
  • La boîte d’une équipe s’active depuis un client : Principal/enableMail pose les six boîtes, l’identité d’envoi, la liste de tâches, le dossier racine et le script de tri qui exigeaient un terminal, répond laquelle de quatre choses s’est vraiment passée (posée, déjà vivante, une pose interrompue complétée, ou un composant à moitié posé et rien de touché, qui est une erreur nommée et jamais un succès), sérialise les activations concurrentes, et annonce ce qu’elle a créé sur le compte de l’équipe pour que les clients connectés voient la boîte arriver. L’annuaire dit si une adresse reçoit (Principal.receivesMail, les listes de diffusion comptent comme joignables), et une équipe qui reçoit sans pouvoir répondre est un échec de doctor qui nomme l’adresse et le verbe qui répare.
  • Écrire une propriété que le serveur ne connaît pas ne répond plus « enregistré » : un nom inconnu, une casse différente, un mot renommé étaient comparés à rien et acquittés. Le refus nomme désormais la propriété, distingue un nom inconnu d’un chemin qui plonge dans une propriété absente, et dit quand une propriété existe mais est calculée par le serveur ou changée par un autre geste. Le durcissement s’active type par type, l’annuaire d’abord (JMAP v2).
  • Dossiers et règles de tri, quatre corrections : un compteur « nouveaux depuis la dernière visite » à côté du total de non-lus (folder_entered_at posé à l’entrée, FolderVisit/get et markViewed, filtre newSinceVisit d’Email/query, annoncé par folderVisit), sur les dossiers système comme personnels ; deux dossiers frères ne peuvent plus porter le même nom et un nom de dossier ne porte aucun caractère réservé, garanti par une contrainte de base après réparation ; une règle peut désigner sa cible par un chemin (Archive/Crisp, annoncé par ruleFolderPath) plutôt que par un nom nu que deux dossiers pouvaient porter (courrier).
  • La recherche exige tous les mots, et l’accent ne décide plus : une requête de plusieurs mots exige désormais tous ses mots, avec un repli sur « l’un des mots » quand cela ne trouve rien, et résumé trouve resume ; Email/query sait trier par pertinence. sendAt et scheduledSendAt n’acceptent plus que la chaîne RFC 3339, ce qui clôt une migration de fil en trois temps. Un message soumis par un compte authentifié garde cette provenance avec lui ; un import sans date de source exploitable prend l’en-tête Date: du message avant l’heure de l’import.
  • Chat : un salon détruit par son propriétaire part dans une corbeille que le propriétaire voit et d’où il restaure (corbeille des salons) ; qui a détruit un salon, retiré un membre, changé un rôle ou modéré un message est désormais au journal d’audit, avec l’adresse et la session d’origine ; le statut de présence gagne une note libre avec un emoji optionnel, annoncée dans la capability chat avec ses deux plafonds ; la surface E2EE keySlots est retirée (zéro ligne mesurée sur six instances) et un message créé avec un schéma de chiffrement autre que none est refusé au lieu d’être stocké en clair.
  • Les journaux se lisent ensemble : oximail journal list lit le journal d’administration et le journal des accès dans une seule chronologie, avec filtres d’acteur et d’objet ; AuditEvent/verify est plafonné à maxEventsPerVerify (2000) et reprend par nextAfter ; une borne de temps sur AuditEvent/verify et /query est retamponnée dans la forme canonique avant d’atteindre le SQL (elle était comparée comme du texte, si bien que 10:30:00Z excluait l’instant même qu’elle nommait) ; la rétention du journal d’administration passe à 365 jours, alignée sur le journal des accès, et un effacement ne touche plus la chaîne ; les extensions journalisent par une porte ext: ; un locataire qui active le journal des accès choisit strict ou au mieux pour une ligne qui ne peut pas s’écrire (conformité, CLI).
  • L’adresse du client derrière un proxy de confiance n’est plus choisie par le client : Forwarded se lit de droite à gauche, les adresses sont analysées ou rien, les en-têtes ne sont jamais crus sans pair TCP, et une requête dont l’adresse ne peut pas être établie est refusée plutôt que servie sous "unknown". check-config refuse une écoute loopback derrière un proxy qu’elle ne reconnaît pas, puisque tous les visiteurs partageraient sinon un seul seau de limitation.
  • Exploitation : /metrics a son propre jeton ([admin] metrics_token_file), distinct du jeton d’administration qu’une configuration Prometheus devait détenir ; les avertissements et erreurs se comptent par module ; restore refuse une sauvegarde faite par un binaire plus récent et tout verbe qui ouvre la base refuse une base migrée par un binaire plus récent ; un espace de noms de migration inconnu est un refus au démarrage ; oximail app-password revoke ferme les sessions ouvertes avec lui ; un harnais de fuzzing couvre les analyseurs de protocole du dépôt et a trouvé deux défauts du lexer Sieve dans sa première minute ; setup verify sort enfin non zéro et dit qui renouvelle le certificat ; l’assistant publie l’IPv6 du serveur (AAAA, ip6:) et ne prend plus une adresse v6 pour une v4 ; aucun verbe CLI n’exige un secret sur sa ligne de commande (exploitation).
  • Mesuré, puis tranché : SQLite alloue désormais chez jemalloc, dans une arène qui lui est propre (il portait 94 % du tas chez glibc) ; le planificateur de rappels d’agenda lisait les événements sous un LIMIT 1000 sans ordre et en sautait 380 sur 1 380 sur la box de production ; le plancher de rétention de 7 jours sur la corbeille et le chat disparaît (la valeur de l’administrateur s’applique, à partir de 1 jour) ; le plancher de posture établie du filtre anti-spam passe à 14 jours sur la foi d’une mesure sur 119 000 messages ; le contrôle d’usurpation de marque ne prend plus un logo pour un lien.
Début septembre
  • Le chiffrement au repos est celui du volume, pas de l’application (ADR-148, cinq lots en neuf jours) : la base SQLCipher, les clés de blobs par compte et l’en-tête auto-descriptif des blobs ont quitté le binaire ; la base est un SQLite ordinaire, les blobs sont leur contenu nommé par son empreinte, et la protection au repos est dm-crypt/LUKS sous l’instance, mesuré par le démarrage, par doctor at_rest et par la jauge oximail_at_rest_volume. L’assistant refuse un volume nu sauf si --accept-bare-volume consigne le consentement, et imprime la configuration de référence en six points. server.key, les clés de tenant, l’escrow et la rotation restent pour les secrets qu’ils enveloppent ; key purge-pending retire désormais tous les reliquats de rotation sous un seul exercice de récupération ; blob verify --walk contrôle le disque contre le registre ; restore refuse un blob scellé et nomme le binaire de son époque. Le déclencheur était une mesure : la mémoire de clés verrouillée coûtait un facteur 6 à 20 sur chaque requête (chiffrement au repos).
  • Le relais n’est pas une archive (ADR-149) : le corps d’un message soumis par un compte relais est un fichier d’un spool dédié ([storage] relay_spool_path, un point de montage à lui, exclu des sauvegardes), pas un blob ; il est délié dans la même écriture terminale que sa ligne de file au 250 du destinataire ou à l’abandon ; le rapport d’échec ne porte rien du message et n’est jamais livré dans une boîte locale ; un MX de secours garde le courrier en transit de la même façon ; sans spool, aucune soumission relais n’est acceptée, et doctor relay_spool mesure la séparation (relais sortant).
  • Le journal des accès aux données est servi (ADR-150, lots 2 à 6) : qui a lu, listé, écrit, partagé, exporté ou détruit quel objet, dans sa propre table chaînée avec sa propre rétention de 365 jours, sous la capability accesslog, activé par locataire et fermé sur échec quand il est actif. L’amendement 1 a suivi un banc refusé (p99 +251 % sur Email/get avec un commit par appel) : le journal trace l’accès par un tiers, jamais les lectures du propriétaire. access-log list --actor répond « qu’a fait tel collaborateur », borné par --since ; le checkpoint WAL a quitté le chemin de la requête (conformité).
  • La chaîne du tenant : un acte du nœud lui-même (appairer une organisation, la suspendre, tourner la clé de travail, enrôler une racine) n’avait aucun domicile dans le journal d’administration ; il a désormais sa propre chaîne avec un enregistrement d’origine, audit-log list|verify|attest, un contrôle doctor qui vérifie aussi chaque chaîne de compte, et une réécriture légitime (rédaction d’effacement, rekey de principal) est un acte de la chaîne et non une rupture. Le créateur d’un principal est enregistré.
  • Meet, la forme servie : un invité externe sans compte rejoint par lien (POST /meet/guest, un identifiant TURN éphémère par invité), la salle d’attente et le rôle de modérateur existent, un participant actif peut faire venir quelqu’un dans un appel déjà commencé, un appel de groupe peut grandir au-delà du maillage direct par un relais média branché sur le seam d’extension (sans relais, le comportement d’aujourd’hui bit pour bit), le serveur répond lui-même au STUN sur un port que l’exploitant ouvre, et une réunion planifiée porte son lien dans l’invitation iTIP, là où la copie de l’invité arrivait sans (ADR-147, Meet). Un étranger à une session reçoit la même réponse qu’un identifiant inventé.
  • Notes, une nouvelle surface : la capture libre du workspace, pour ce qui n’est pas une tâche (si ça a un état « fait », c’est une tâche, sinon une note). Notebook est l’unité de partage, avec un vocabulaire de droits à trois bits qui lui est propre ; une Note porte un body ProseMirror canonique et un bodyText dérivé par le serveur, servis seulement s’ils sont nommés, des étiquettes libres, des liens vers d’autres objets du workspace qui ne cassent jamais, une corbeille en deux temps et [notes] trash_retention_days. La dictée est un Note/set create avec une pièce jointe audio derrière un seam de moteur : sans moteur, la page est acceptée et répond transcription: "failed" avec sa raison, jamais en silence. Note/transcribe ajoute un enregistrement à une page existante ou relance une transcription en échec, et chaque pièce audio porte la provenance de sa propre transcription, fournisseur, hôte contacté et un palier de résidence (local, ch, eu, non_eu) sur lequel le client branche, écrite sur la tentative ; la page n’affirme pas davantage que la moins souveraine de ses pièces (ADR-144, Notes).
  • Invitations de réunion : le message d’annulation a la forme de l’invitation, dans la langue du destinataire et à sa pendule ; CalendarEvent/set ajoute un lien de visioconférence à une réunion qui existe déjà au lieu de répondre updated sans rien faire.
  • Boîtes et agendas partagés depuis tous les clients : une boîte partagée apparaît en IMAP sous Other Users/<adresse>/…, un agenda partagé en lecture ne s’annonce plus modifiable à CalDAV, un message se copie d’une boîte partagée vers la sienne avec son historique, marquer un message à supprimer dans une boîte partagée exige le droit de supprimer, un partage mayWrite n’implique plus mayDelete sur le Drive, et une requête qui mélange son propre compte et un compte partagé est refusée au lieu de répondre depuis le mauvais. Les mandats d’un compte gelé sont suspendus, ceux de l’opérateur restent. EventSource pousse aussi les changements des comptes partagés.
  • Lot de conformité protocolaire (mesuré contre de vraies boîtes) : IMAP répond BAD à une commande hors de son état, sert les THREAD, SEARCH MODSEQ, STATUS SIZE et QUOTA qu’il annonce, honore QRESYNC sur EXAMINE, accepte un littéral dans toute commande, refuse ce qu’il ne comprend pas, et les verbes ACL sont refusés plutôt qu’acquittés à vide quand la face partagée est éteinte ; SMTP appose Return-Path et une clause for, et MAIL refuse ce qui n’est pas un reverse-path ; un REPORT CalDAV/CardDAV que le serveur ne sait pas nommer est refusé au lieu de répondre avec toute la collection, un segment d’URL qui n’est pas un compte n’est pas un principal, et l’en-tête DAV: n’annonce plus la classe 2 ; les trames d’erreur WebSocket ont la forme de la RFC 8887 ; SearchSnippet/get calcule notFound quel que soit le filtre ; accountId est requis en lecture comme en écriture ; un require Sieve d’une extension inconnue est refusé selon la RFC 5228 (protocoles historiques).
  • Cloisonnement entre comptes : sept écarts mesurés sur la flotte lors d’une campagne de conformité, corrigés la même semaine ; un téléchargement de blob porte ETag et Cache-Control puisque son identifiant est son empreinte ; un lien de partage public ne peut plus être sans fin.
  • Surface d’exploitation : une clé de configuration inconnue est une erreur et un bloc répété s’imbrique (ADR-007 Am. 2) ; doctor charge enfin les sections d’extension que tous les autres verbes lisaient déjà ; un binaire étendu peut ajouter des verbes CLI et des contrôles doctor au lieu de les intercepter ; la corbeille du Drive a un réglage par locataire ([drive] trash_retention_days), un verbe et une seule sortie qui respecte les séquestres, et les quatre autres corbeilles (tâches, contacts, événements, pages) passent par elle ; la rétention des versions de fichiers est un réglage par locataire appliqué à chaud ; oximail drive share partage un seul dossier depuis la CLI et rights audit voit les fichiers ; le retour arrière d’un déploiement se prouve avant le swap par le binaire cible ; les deux cents commandes d’administration ouvrent une connexion au lieu d’un pool (quatre invocations sur dix plantaient sur cinq box sur six) ; la sauvegarde et la vérification des blobs ne vident plus le cache de pages ; supprimer un lot de messages libère ses pièces jointes en secondes et non en jours ; le courrier accentué atteint un récepteur sans 8BITMIME par dégradation au lieu d’être perdu sans rebond ; une adresse se résout par un seul chemin.
  • Les fichiers, aux portes : un fichier tenu ouvert par un protocole qui a un « open » (un moteur SMB branché dans le même binaire) est refusé aux rédacteurs JMAP avec forbidden, en nommant le porteur et l’âge du verrou, et FileNode/get sert lockedBy ; le registre est en mémoire, dans le processus, donc un verrou meurt avec la connexion ; un fichier créé sans contentType reçoit le type que son nom implique, un type explicite gagne toujours (fichiers).
  • Sieve dit ce qu’il implémente : l’objet de capacité valait {} ; il porte désormais le nom de l’implémentation, les plafonds de taille de script et de nom et la liste des extensions depuis les constantes que le compilateur applique, null là où rien n’est plafonné ; un require d’extension absente est refusé à la compilation en nommant ce qui est disponible, et les trois comparateurs peuvent être exigés ; onSuccessActivateScript est honoré dans la transaction des écritures qu’il suit au lieu d’être ignoré pendant que la réponse disait « créé » ; activer un script qui n’existe pas ne change rien, là où cela désactivait d’abord tous les autres (Sieve).
  • Licence et doctrine : le serveur sera publié en fair source sous l’OxiMail License, dont chaque version repasse en AGPL quatre ans après sa parution (ADR-142, qui remplace ADR-004) ; le navigateur ne parle qu’à sa propre origine et tout appel vers un tiers passe par le serveur (ADR-143) ; un verdict anti-spam porte son axe pour qu’une tolérance de renvoi puisse re-disposer par destinataire sans mentir au scoreur (ADR-146) ; les vestiges E2EE d’ADR-020 ont quitté le schéma.
Fin août
  • Meet grandit : des liens d’invitation (un par réunion, rachetable N fois), la salle d’attente et le modérateur qui agit sur autrui, un maxParticipants appliqué, une fin de session qui ne réécrit plus l’histoire, un invité qui écrit dans le chat de l’appel, des lignes de réunion enfin conservées et effacées, Preferences qui accepte la section meet où deux fonctions clientes écrivaient déjà, et le lien de jonction fabriqué avec le bon hôte dans les invitations sortantes (Meet).
  • Le temps, une seule fois : le cœur portait deux moteurs de calcul des répétitions, l’un sensible au fuseau et l’autre non, et une série hebdomadaire à 9 h traversait un changement d’heure décalée d’une heure ; il n’y en a plus qu’un. Une seule façon de lire une heure murale, avec l’écart du changement d’heure lisible dans la zone. Un horaire de travail sait dire « de 22 h à 6 h le lendemain » (endDayOffset, ADR-139), le client demande d’abord au serveur s’il comprend la notion, et la disponibilité d’un jour se calcule sur la veille autant que sur lui-même. Le fuseau d’une personne peut être retiré, s’écrivait sous un nom que le serveur ne lisait pas, et le fuseau par défaut d’une organisation se corrige depuis un client ; doctor calcule la prochaine bascule par fuseau utilisé et compte ce qui tombe dans l’heure qui n’existe pas ; la case « rappels d’agenda » coupe enfin quelque chose ; un incident de disponibilité laisse quelque chose à regarder. Un séminaire de trois jours n’est plus un agenda libre.
  • Seams d’extension (ADR-137, ADR-138) : des comptes système sans surface mail, un verbe administratif calendar share, des collections résolubles sur le préfixe shared:, et un contributeur externe optionnel au free-busy d’un principal dont l’échec est serverFail plutôt que « libre ».
  • Anti-spam, les mécanismes nommés par une mesure à J+7 : un renvoi par règle ne blanchit plus le spam détecté ; le verdict du modèle n’étouffe plus les autres signaux, et un correspondant établi ne part plus en indésirable sur la seule opinion du filtre statistique ; un envoi en Cci massif hors de la liste de TLD atteint la bande suspecte ; le bouton « indésirable » du webmail entraîne enfin le filtre dans les deux sens ; un verdict perdu à la livraison n’est plus avalé ; la signature de désabonnement en un clic n’est plus un passe-droit au-dessus du seuil ; les réponses des DNSBL sont lues, pas crues ; la relation avec un expéditeur se lit dans les deux sens ; un geste qui contredit le filtre est une opinion, pas une leçon ; un statut de transaction n’est pas un nom d’expéditeur ; une usurpation de banque sans rien qui lui appartienne, quatre tromperies de lien et les liens cachés dans du HTML encodé sont vus ; un faux avis de livraison en allemand ne passe plus ; l’hameçonnage n’est plus rangé avec la publicité ; le courrier renvoyé par votre ancien fournisseur n’est plus condamné par construction ; l’alerte sur les rapports DMARC dit ce que le rapporteur a expliqué (anti-spam).
  • Annuaire et équipes : une équipe peut avoir sa propre adresse (réservée à la création, plus adossée en silence au compte de son créateur) ; les collègues portent leur nom dans le carnet d’adresses ; les champs de profil ne vivent plus que sur la fiche d’annuaire, semée depuis les profils existants ; un groupe de distribution ne peut plus éclipser une adresse existante ; le départ d’un collaborateur tient en une commande ; désactiver un compte ferme enfin la porte, session IMAP ouverte comprise, et désactiver le dernier administrateur n’est plus une porte à sens unique ; le séquestre légal a sa première étape.
  • Courrier et recherche : chaque message connaît sa langue, un ancien à l’ouverture sans backfill, et la langue est un critère de recherche ; « re: facture » cherche vraiment (un deux-points n’est plus un nom de champ) ; une conversation en sourdine le reste dans le temps ; la surface des quotas est une liste de vrais chiffres plutôt qu’un objet unique au plafond fictif ; un refus de quota dit lequel et de combien ; l’accusé de remise fonctionne avec un envoi annulable ; un nom d’affichage encodé à virgules n’explose plus en expéditeurs vides ; un filtre non supporté et une mauvaise valeur de filtre sont deux erreurs distinctes ; le plafond de téléversements simultanés annoncé est tenu ; un /changes filtré ne nomme plus des messages qui ne l’ont jamais concerné ; le push en flux ne perd plus sa fenêtre ; @here, @channel et @all préviennent enfin quelqu’un ; une réaction ne peut plus peser un mégaoctet ; les emojis personnalisés sont bornés et peuvent être éteints par organisation ; les stickers ont leur calibre.
  • Drive et fichiers : les dates des documents migrés survivent ; le responsable d’un espace partagé n’en perd plus la main et sait quand il est passé outre un partage ; le premier dossier d’un espace partagé ; trouver un fichier dans un grand dossier coûte le même prix que dans un petit ; un fichier de 27 Go ne passe plus par la mémoire (ADR-145) ; un carnet ré-importé sans identifiants ne duplique plus ; « dernier échange » regarde en arrière ; le carnet d’adresses répond aux questions qui font son utilité, et ses colonnes et vues enregistrées vivent sur le compte.
  • Ce qui a été retiré, ou ramené au vrai : six réglages qui annonçaient une garantie que rien ne consultait, un contrôle vert qui ne prouvait rien, trois boucles de retour publiées et mortes, un service qui annonçait un confinement qu’aucune machine n’appliquait, un client de migration qui acceptait n’importe quel certificat, un service durci qui ne pouvait pas lire sa propre clé, deux ports qui se seraient ouverts sans qu’on le demande, un diagnostic qui présentait un contrôle non exécuté comme réussi, des noms DNS et HTTPS qui pouvaient diverger, un certificat qui ignorait un nom ajouté, des pages publiques sans leurs protections de navigateur, un journal d’exploitation noyé sous 144 000 avertissements, et un journal qui enregistrait à quelle vitesse un client lisait son courrier. La clé racine en clair sur un volume nu est nommée, et sa purge redevient atteignable. Le courrier effacé quitte les fichiers de l’index de recherche, pas seulement l’index.
  • CLI : les commandes d’administration démarrent deux fois plus vite ; un nouvel export écrit un fichier par message (neuf secondes là où il fallait deux heures pour cinq mille) ; la taxonomie des commandes est tranchée et gardée (blob comme racine unique) ; un verbe d’administration ne suit plus jamais le wildcard de livraison (un set-password sur une adresse inexistante avait remplacé le mot de passe du propriétaire du catch-all) ; une commande d’administration ne fabrique plus de clé racine par mégarde ; le MX de secours démarre par le même chemin que les autres ; les listes de noms DNS et HTTPS de l’assistant viennent d’une seule source.
  • L’observation, bornée (ADR-140) : le niveau de journalisation ne se monte que pour une durée bornée, oximail log elevate --directives … --duration-secs N ou PUT /admin/v1/logs/level, 60 à 3600 secondes, et il revient de lui-même, l’élévation et le retour étant deux événements du journal (debug était allumé depuis avril sur la plus vieille box de production, jamais éteint) ; le projecteur de trace protocolaire capture l’échange brut d’un compte ou d’un pair, jamais tout le monde, pour 60 à 900 secondes, caviardé, 0600 sur le tmpfs, purgé à l’arrêt ; log tail rend le journal une ligne lisible par événement (exploitation, API d’administration).
  • Dit nommément, pas avalé : un contact créé avec des propriétés que le serveur ne connaît pas répondait succès et existait sans nom, il est refusé en les nommant ; Rule/test, Rule/apply et le dry-run de la CLI lisent le verdict anti-spam stocké au lieu de « non noté » ; une tâche porte son timeZone, si bien qu’un DUE:…Z venu de CalDAV ne flotte plus et qu’un rappel sonne à l’instant de la tâche ; le flux temps réel perdait une fenêtre en attente quand un client se réabonnait, l’abonnement ne remplace plus que la liste des types suivis ; un import IMAP ancre sa reprise sur l’UIDVALIDITY de la source, si bien qu’une renumérotation ne saute ni ne duplique, et une source qui n’en annonce pas échoue plutôt que de reprendre sur des clés ambiguës (contacts, tâches, migration).
Mi-août
  • Meet, première forme qui fonctionne : les appels en temps réel gagnent une surface serveur, à savoir une session ancrée sur un salon de discussion, dont la création atteint tous les membres du salon pour qu’un appelé apprenne l’appel sans type de notification dédié, plus un relais transitoire pour la poignée de main WebRTC, jamais persistée ni journalisée. La phase 1 est en pair-à-pair jusqu’à trois participants actifs, et l’adhésion est un bail de présence à expiration automatique plutôt qu’un drapeau (Meet).
  • Email/get gagne son arbre : bodyStructure est émis et récursif, fetchAllBodyValues atteint enfin toutes les feuilles text/* (la partie text/calendar d’une invitation comprise), et les pièces jointes portent partId, charset et disposition. Les en-têtes par partie sont servis sous bodyProperties, seul chemin conforme vers les paramètres de Content-Type qui distinguent une invitation d’un .ics joint. Changement de contrat : sans argument fetch*BodyValues, bodyValues revient désormais vide comme l’exige la RFC 8621 §4.2, alors qu’il était rempli inconditionnellement (l’arbre du corps).
  • Chaque /get ferme son vocabulaire, et le canon est publié : un nom de propriété inconnu devient un invalidArguments qui nomme les fautifs au lieu d’un mot ignoré en silence, et ce qui est sélectionnable, comme ce qui est calculé par appelant, est désormais émis en forme machine à côté des types générés (le canon de projection).
  • Les boîtes partagées se lisent et s’écrivent correctement : la clé de déchiffrement suit le compte effectif et non l’appelant, si bien que les corps d’une boîte partagée s’ouvrent ; et un message classé par un bénéficiaire est chiffré pour le propriétaire, ce qui referme le miroir du même défaut. La couche de clés elle-même est partie avec ADR-148 trois semaines plus tard ; la règle du compte effectif lui survit comme périmètre du registre des blobs.
  • Thread/get et Thread/changes reviennent aux clients RFC 8621 purs, servis sous la capacité mail là où le §3 les place ; un client ne déclarant que core + mail s’entendait répondre unknownCapability sur une méthode obligatoire (courrier).
  • Les annuaires de contacts de tenant : une organisation peut publier des jeux de contacts partagés qui atteignent les membres en fiches lisibles seulement dans leur propre carnet, avec des entrées tenues par des principaux et des groupes nommés (contacts, ADR-133).
  • L’échange de clés post-quantique est réellement négocié : X25519MLKEM768 est proposé en tête, avec le X25519 classique en repli, sur 25, 465, 587, 993 et 443. La fonctionnalité était compilée depuis des mois alors que le fournisseur installé ne pouvait pas l’implémenter : chaque session retombait en silence (TLS & ACME).
  • Deux ans de chiffrement amont rattrapés : SQLCipher passe de 4.5.7 à 4.14.0, SQLite de 3.45.3 à 3.51.3, et le binaire livré est contrôlé au déploiement pour ne dépendre d’aucune bibliothèque de cryptographie, TLS ou SQLite du système. Trois semaines plus tard, ADR-148 a retiré SQLCipher du binaire (début septembre, ci-dessus) ; le contrôle de dépendances lui survit.
  • Les journaux de poignée de main cessent de crier au loup : les quatre écouteurs TLS séparent un pair qui a raccroché avant de négocier d’un vrai échec de négociation, chaque branche portant un champ reason stable. Une instance de production émettait 480 avertissements inoffensifs par jour pour une seule sonde de supervision, et un vrai échec s’y serait caché (exploitation).
  • dkim=fail dit pourquoi : le motif d’échec atteint l’en-tête Authentication-Results et le résultat conservé par message, si bien qu’une analyse après coup sépare une signature invalide d’un corps modifié en transit, d’une clé absente, ou d’une signature datée dans le futur, ce qui signifie que l’horloge du serveur retarde (authentification du courrier).
  • Exploitation : oximail sql route sur la forme du statement, si bien qu’un WITH ... SELECT retourne des lignes au lieu de rapporter « 0 rows affected » ; blobs gc ne collecte plus un blob non référencé plus jeune qu’une fenêtre de grâce de 24 heures (RFC 8620 §6.1) ; les instantanés locaux de retour arrière ont gagné une politique de rétention que le déploiement applique ; doctor juge la santé de l’horloge ; et des limites seulement annoncées sont devenues des limites qu’un mécanisme applique (CLI, exploitation).
  • Fuseaux horaires : le fuseau par défaut d’un tenant est lisible, hérité et surveillé, et un fuseau fourni au provisioning atteint désormais le principal. Un fuseau invalide est refusé avant la première écriture au lieu d’être accepté puis ignoré.
Fin juillet et début août
  • La fédération, première forme qui fonctionne : la cérémonie d’appairage bilatérale hors bande (empreinte sur le realm et la clé, clé de nœud par organisation), une racine d’identité portable distincte de la clé de travail jetable qui vit sur la machine (la rotation vaut révocation, par époque monotone), et le modèle de grant inter-organisation : borné au realm, écrit par le propriétaire, à expiration automatique, et qui n’élargit que l’axe d’accès. Révoquer un appairage suspend l’accès au lieu de détruire l’enregistrement ; les notices d’effacement sont estampillées sur l’ancre d’identité et échouent fermé à la livraison (ADR-124 à ADR-127).
  • Les accusés de lecture, sur les deux surfaces. Le courrier gagne la surface MDN de la RFC 9007 : MDN/send compose un accusé comme geste explicite de l’utilisateur, MDN/parse rend enfin lisibles les accusés entrants déjà rattachés à vos messages envoyés. Les accusés de lecture du chat atteignent désormais en direct chaque membre d’un groupe ou d’un canal, et non plus le seul pair d’une conversation directe, avec un interrupteur de visibilité par canal et un plafond de taille de salon sur la diffusion en direct (chat).
  • API d’administration : la liste des expéditeurs de confiance d’un compte, l’ensemble que les envois sortants et les sauvetages de ham écrivent aussi, se lit et se modifie (GET/POST/DELETE /admin/v1/accounts/{id}/trusted-senders), puisqu’une règle permanente que personne ne peut lister est invisible ; un connecteur d’annuaire externe pousse les contacts de l’organisation dans les comptes qu’il nomme (POST /admin/v1/contacts/org-sync) (API d’administration).
  • L’historique de versions du drive sur le fil : FileVersion/query, get et restore, avec maxVersionsPerNode annoncé et l’élagage piloté par la même constante (fichiers).
  • L’honnêteté de la synchronisation : un curseur Foo/changes plus ancien que la fenêtre de rejeu de 90 jours est maintenant refusé avec cannotCalculateChanges, au lieu de s’entendre dire qu’il est à jour. C’est un seul point de passage au niveau du stockage dont tous les consommateurs héritent, sync-collection de DAV compris. /changes réduit aussi à une seule issue par identifiant, conformément à la RFC 8620 §5.2, et un lot /set vide ne saute plus sa vérification d’ifInState (JMAP Core).
  • Les identifiants de streaming : les flux SSE s’ouvrent avec un ticket éphémère de 60 secondes au lieu du token de session, pour qu’un identifiant valable 24 heures ne circule plus dans une URL que les proxies journalisent.
  • Les formes de fil corrigées là où elles laissaient filtrer le stockage : les aperçus de liens du chat sont des objets typés et non plus un tableau d’octets bruts ; une partie message/rfc822 est annoncée comme la pièce jointe qu’elle est, au lieu de publier au premier niveau les pièces jointes du message encapsulé ; quatre internes de stockage de l’agenda ont quitté le fil.
  • IMAP : FETCH RFC822, RFC822.HEADER et RFC822.TEXT ne renvoyaient aucune charge utile, et RFC822 marquait quand même le message \Seen. Les deux sont corrigés (protocoles historiques).
  • L’intégrité de la rétention et des sauvegardes : la sauvegarde par organisation n’omet plus en silence les blobs de pièces jointes ni les lignes des tables absentes d’une liste tenue à la main, et verify-backup peut voir la différence ; la corbeille vers laquelle balaient les messages qui expirent a enfin une politique de rétention derrière elle ; les blobs de parties de corps sont devenus le cache dérivé qu’ADR-098 décrivait depuis toujours, re-dérivé et vérifié par empreinte lors d’un échec de téléchargement (exploitation).
  • L’exploitabilité : onze tâches de ménage au démarrage ne se marchent plus dessus en sqlite busy avec un tick perdu à chaque boot ; doctor a cessé de présenter toute la file de remise comme en attente ; un verbe spam check en lecture seule demande au filtre son opinion actuelle sur un message stocké ; les DSN et ARF entrants lèvent désormais des événements de rebond et de plainte sur le chemin d’ingestion.
  • Les portées des mots de passe d’application sont appliquées de bout en bout : la portée voyage avec le token et survit au renouvellement, une seule matrice de capacités décide, et l’accès aux protocoles se déduit de l’ensemble des capacités, appliqué au dispatch JMAP, à la connexion IMAP et à DAV (ADR-129).
  • Anti-spam : le greylisting au moment du RCPT a été retiré comme défense. Les botnets réessaient, les pools d’adresses des gros expéditeurs cassent le triplet, et le mécanisme taxait les expéditeurs légitimes de premier contact. L’exigence d’authentification et le pipeline scoré portent cette charge (ADR-128, anti-spam).
  • Installation : l’assistant a gagné un profil smarthost comme troisième entrée, consigné comme marqueur d’intention audité et non comme un rôle (premier démarrage).
  • Domaines d’enveloppe : une plateforme qui envoie par le MSA peut utiliser un MAIL FROM technique sur un domaine prouvé par DNS, formes VERP comprises, pendant que l’en-tête From: reste l’identité humaine. Le domaine accordé doit partager son domaine organisationnel avec une identité du compte : l’alignement SPF sous DMARC relaxed tient donc par construction (envois en nombre).
Début juillet
  • Conformité : la journalisation du courrier par organisation (entrant/sortant, boîte ou archiveur externe), et l’effacement de compte reconstruit en machine à phases durable et reprenable, avec tampon de réversibilité, preuve DSAR (oximail erasure prove) et rétention appliquée du journal d’audit.
  • Performance : une campagne de test de charge mesurée a environ triplé le débit de lecture (cache d’instructions préparées par connexion) et doublé l’ingestion saturée (index composite de fils) ; l’ingestion d’un message isolé a perdu son plancher de latence fixe (l’index de recherche valide au repos de la file) ; le WAL est borné et observable.
  • Justesse de la remise : la visibilité de synchronisation incrémentale de l’ingestion est devenue atomique avec la ligne du message ; les règles du destinataire et l’absence s’exécutent désormais sur la remise locale (ADR-117) ; les sauts sortants font la médiation des extensions ESMTP (SMTPUTF8/8BITMIME/SIZE) au lieu de combler en silence ; DKIM sursigne From/To/Subject.
  • Vague d’interopérabilité clients (vérifiée contre Apple Calendar, Thunderbird, eM Client, les invitations Outlook) : amorçage DAV à la racine, XML sûr en CDATA, ETag en empreinte de contenu, vrais filtres calendar-query, émission des VTIMEZONE, modélisation des VALARM, annonces IMAP EXPUNGE correctes, et un point d’accès de configuration eM Client.
  • Les surcharges de configuration se listent : GET /admin/v1/config renvoie les surcharges en vigueur et knownKeys, l’ensemble fermé des clés que le code consomme, si bien qu’une faute de frappe ne devient pas un réglage silencieux (API d’administration).
  • Authentification : DPoP v2 : nonce serveur, preuves par requête, et le jeton média mono-blob.
Mai et juin
  • Relais smarthost sortant (ADR-116) : comptes de relais aux domaines prouvés par DNS, enrôlement à usage unique, quotas de destinataires par jour avec montée en charge, rétention éphémère du relais, auto-surveillance DNSBL (relais sortant).
  • Lot sécurité : vérification des mots de passe d’application à travail constant, durcissement SSRF des récupérateurs de push et d’aperçus, dispatch des comptes partagés fermé par défaut, autorisation du From: d’en-tête à la soumission, hachage au repos des tokens.
  • Refonte du pipeline anti-spam : époques et reconstruction du corpus Bayes, liste blanche des réponses sur enveloppe authentifiée seulement, colonne vertébrale de disposition (anti-spam).
  • Drive et collaboration : drive raciné avec favoris par lecteur et vignettes serveur, comptabilité des liens de partage, chronologie de conversation, plafonds de capacité du chat et saisie limitée aux membres.
  • Balayage de conformité du fil : la classe des « ignorés en silence » de /query éliminée (filtres, tris et arguments inconnus sont des erreurs), les capacités d’extension annoncées honnêtement dans accountCapabilities.
Avril : v0.30.0 La version de durcissement d’avant lancement : la discipline d’échec bruyant passée sur chaque surface, l’isolation par organisation dans la couche de stockage, le chiffrement au repos par défaut, le modèle SMTP à deux chemins, les passerelles historiques, et l’assistant d’installation tel que livré aujourd’hui. Pour distinguer le réel du spécifié : chaque page de cette documentation énonce la version qu’elle décrit. Le site documente la ligne v0.30.0 telle que déployée, jamais du travail prévu présenté comme livré.