Skip to main content
Une plateforme de listes de diffusion (MailWizz, Listmonk, Mautic…) qui envoie via votre OxiMail a besoin que l’expéditeur d’enveloppe diffère du From visible :
  • From: reste l’adresse lisible que les destinataires voient et à laquelle ils répondent.
  • Return-Path: (le MAIL FROM d’enveloppe) est une boîte technique dédiée qui collecte les notifications de statut de livraison, pour que le traitement des bounces ne fouille jamais le courrier ordinaire d’une personne.
C’est l’arrangement standard de l’envoi en masse : tous les expéditeurs commerciaux fonctionnent ainsi (Amazon SES parle de custom MAIL FROM domain, Postmark de custom Return-Path). Par défaut OxiMail le refuse, à juste titre : un compte authentifié ne peut utiliser que ses propres identités comme MAIL FROM, et une boîte de bounce n’est volontairement pas une identité de l’expéditeur. Les grants de domaine d’enveloppe ouvrent exactement cet arrangement, et rien de plus. Ce dont une plateforme qui envoie à grande échelle a besoin au-delà du return-path (une identité propre, sa propre adresse, une file qui garde le courrier des personnes en tête, les exigences des fournisseurs) est plus bas.
Un grant élargit l’enveloppe seulement. Le header From: garde sa propre porte : il doit toujours être une identité du compte authentifié. Et un grant n’est pas un alias : il n’implique aucune délivrance et ne crée aucune identité. C’est de l’autorisation pure.

Comment ça marche

Un grant autorise toute local-part d’un domaine comme MAIL FROM. La granularité domaine est délibérée : le traitement des bounces frappe normalement une enveloppe différente par destinataire (VERP, bounce+alice=example.com@bounces.example.org), donc des règles par adresse ne pourraient jamais suivre. Un domaine accordé couvre toutes les variantes VERP. La preuve appartient au domaine du tenant, comme toute preuve de domaine d’envoi : une fois qu’elle tient, tout compte ordinaire du tenant peut utiliser le domaine comme MAIL FROM, et le compte que vous nommez à add est celui dont les identités servent à vérifier la règle de même organisation ci-dessous. Deux garde-fous s’appliquent à la création du grant, pas découverts plus tard :
  1. Le courrier du domaine doit revenir à ce serveur. Le grant n’autorise rien tant que tous les enregistrements MX du domaine ne nomment pas le nom d’hôte du serveur ([server] hostname), ou que le domaine n’a pas de MX et est ce nom d’hôte (le MX implicite de la RFC 5321 §5.1, qui n’exige aucun changement DNS). Un return-path est l’endroit où arrivent les bounces et les plaintes : la preuve qui compte est donc qu’ils arrivent ici. Elle remplace l’enregistrement TXT _oximail-envelope que les grants demandaient auparavant. Un MX ailleurs, ou un MX nul, suspend le grant fail-closed ; une erreur DNS transitoire ne le fait jamais. La preuve est revérifiée deux minutes après le démarrage du serveur, puis toutes les six heures, et une preuve qu’aucun contrôle n’a confirmée depuis 72 heures (des serveurs de noms qui ne répondent plus) cesse aussi d’autoriser. Dans les deux cas, les envois sous ce domaine sont différés 451 4.7.1, pas rejetés : la file de la plateforme retente pendant que la preuve revient. La métrique oximail_envelope_domain_suspended_total compte les suspensions, et oximail doctor contrôle chaque preuve sous sender_domain_proofs.
  2. La règle de même organisation. Le domaine accordé doit partager son domaine organisationnel avec une identité du compte. bounces.example.org peut être accordé à sender@example.org ; bounces.other.net non. C’est la règle d’Amazon SES, et c’est ce qui garde SPF aligné sous DMARC par construction : un Return-Path hors organisation casse DMARC en silence chez certains récepteurs des semaines plus tard, alors OxiMail le rend irreprésentable plutôt que de le documenter en avertissement.
Les grants sont provisionnés par l’opérateur (CLI). Un compte ne peut pas s’accorder l’exemption qui le gate.
Un grant créé quand la preuve était un enregistrement TXT est jugé par le MX dès son prochain contrôle, et suspendu si aucun MX ne nomme le serveur. Déléguez le domaine (ci-dessous) avant de vous appuyer sur un grant ancien.

Mise en place

Disons que la plateforme s’authentifie comme sender@example.org et que vous voulez collecter les bounces dans une boîte dédiée.
Pointez le traitement de bounces de la plateforme sur la boîte de bounce@ (IMAP), et réglez son Return-Path sur une adresse du domaine accordé. Terminé : la plateforme envoie comme sender@, les destinataires voient sender@, et chaque DSN atterrit dans bounce@. Un rapport qu’OxiMail rédige lui-même porte le Message-ID d’origine dans sa troisième partie (les en-têtes d’origine par défaut, le seul Message-ID pour un message expiré dans la file), et c’est par lui qu’une plateforme comme MailWizz attribue un rebond ; une soumission qui n’avait pas de Message-ID en a reçu un du serveur à la soumission.

VERP sans aucun travail serveur supplémentaire

Si la plateforme frappe des enveloppes par destinataire (VERP), faites-lui utiliser un plus-tag de la boîte de bounce : bounce+<abonné>@bounces.example.org. Le grant autorise chaque variante (toute local-part du domaine passe), et le plus-addressing d’OxiMail les délivre toutes nativement dans la boîte de bounce@. Pas de catch-all, pas de configuration serveur. La répartition des rôles est délibérée, et c’est celle de toute l’industrie : le serveur de mail autorise et garde DMARC aligné ; la plateforme de listes frappe les enveloppes et dépouille les bounces. OxiMail ne génère pas d’adresses VERP et ne gère pas la cadence d’envoi : votre plateforme fait déjà les deux, et les règles des grands récepteurs (désabonnement en un clic, plafonds de taux de plainte) sont aussi ses obligations : un flux de masse vérifie que les en-têtes de désabonnement en un clic sont présents, il ne les écrit jamais.
Le budget sortant s’applique au compte de la plateforme, et il se règle par compte. [rate_limit] smtp_outbound_rcpt_per_hour (150 destinataires par heure par défaut) est la valeur d’instance dont chaque compte hérite ; au-delà, une soumission est différée 452 4.7.1 et la plateforme réessaie, si bien qu’une campagne de 5 000 destinataires s’écoulerait à 150 par heure. Donnez au compte qui envoie son propre budget plutôt que de relever toute la box (courrier et SMTP) :
Le même budget se lit et se règle depuis une console d’administration sous outboundRcptPerHour sur Principal/getAdmin / Principal/setAdmin (null = la valeur d’instance, 0 = sans limite). Chaque report est compté par compte dans oximail_smtp_outbound_budget_deferred_total{account} et nommé dans le journal, si bien qu’une plateforme bridée se voit avant que son client ne le dise ; un message dont la seule liste de destinataires dépasse le budget est refusé avec une erreur qui dit qu’il ne pourra jamais passer tel quel.

Quand un envoi est refusé

Un MAIL FROM qui ne correspond à aucune identité et à aucun grant jamais vérifié est rejeté en fin de DATA :
Consultez le journal du serveur : si un grant pour ce domaine existe mais n’a jamais été vérifié, le log le dit explicitement (« one step from done ») au lieu de ressembler à une tentative de forge. Lancez envelope-domain verify et revérifiez le MX du domaine. Un grant vérifié puis suspendu, ou non revérifié depuis 72 heures, répond plutôt 451 4.7.1 (« Envelope-sender domain proof is not current ») : un état réversible, donc une réponse transitoire que la plateforme retente dans sa propre fenêtre d’abandon. envelope-domain list l’affiche comme suspended ou stale ; rétablissez le MX ou le DNS du domaine, puis lancez envelope-domain verify.

Retirer un grant

Le retrait est immédiat : les envois sous ce domaine sont rejetés avec le 550 ci-dessus. --yes est requis, comme pour tout verbe destructif.

Envoyer à grande échelle

Une plateforme qui envoie des milliers de messages n’est pas une personne, et la même box porte les deux. Quatre mécanismes les séparent. La route, la classe, la signature et les en-têtes décrits ici sont servis aujourd’hui ; l’admission (la décision d’accepter ou de refuser une soumission selon son flux) ne l’est pas encore : voir l’encadré plus bas.

Les flux d’envoi

Un flux est l’unité qui envoie. Il appartient à un compte, et c’est l’identifiant qui ouvre la session SMTP qui le choisit : un mot de passe d’application lié au flux, ou le mot de passe du compte lui-même pour l’unique flux créé avec --account-login. Un flux a un rôle, exclusif sur un même compte :
  • origination : une application qui émet. Son courrier est à compléter et à signer par le serveur (avec une clé de l’opérateur ou une clé déléguée, et une cosignature si son pool en a une). Quand l’admission sera appliquée, son From: sera jugé d’après les domaines prouvés du tenant plutôt que d’après les identités du compte.
  • transit : une leaf qui signe elle-même, transférée à l’octet près comme l’est le courrier d’un compte relais. Aujourd’hui, ce comportement est décidé par le rôle service du compte.
Tout verbe qui écrit est une simulation tant qu’il n’a pas --apply (--yes pour suspend et delete) ; list et show --flow <id ou nom> acceptent --json. Le nom est unique sur le compte (A-Za-z0-9._-, 1 à 64 caractères). --bulk déclare un envoi de masse. --retention kept|ephemeral (kept par défaut) est enregistré sur le flux ; ce qui rend éphémère le courrier d’un compte relais reste son rôle service (rétention), pas ce champ. suspend --flow … --reason … --yes met un flux en pause, resume --apply lève la pause, delete --yes est refusé tant qu’un mot de passe d’application est lié au flux. Un compte qui porte des flux, atteint par un identifiant qui n’en désigne aucun, n’est jamais pris pour une personne. Ce qu’un flux change aujourd’hui :
  • sa classe dans la file (plus bas) : masse si le flux est déclaré de masse, transactionnelle sinon, et une soumission sans flux reste celle d’une personne ;
  • son adresse : le pool de sortie nommé par set-pool, pour le courrier mis en file à partir de là (un DSN ou un rapport de relais hérite de la route de la ligne à laquelle il répond ; une soumission JMAP ne porte aucun flux et part de l’adresse par défaut) ;
  • ses en-têtes : le message d’un flux d’origination part avec X-OxiMail-Flow: <id du flux> (c’est ainsi qu’une plainte retrouve son flux) et, quand le client n’en a mis aucun, un Feedback-ID: <flux>:<tenant>:<bulk|tx>:<nom d'hôte du relais> (identifiants opaques, aucune donnée personnelle). Un X-OxiMail-Flow écrit par un client est retiré de toute soumission ordinaire : une valeur copiée imputerait à un autre flux ses plaintes ;
  • ses compteurs : les destinataires distants livrés, et les plaintes, par flux (plus bas).
L’admission tourne en observation (shadow) ; elle n’est pas encore appliquée. Toute soumission, SMTP ou JMAP, est aussi jugée par un objet d’admission unique (compte désactivé, nombre d’en-têtes, From:, expéditeur d’enveloppe et ses preuves, flux en pause, garde de masse), et la décision historique du serveur décide toujours. oximail doctor compte les jours où les deux s’accordent sous admission_shadow (sept jours sans divergence sont le critère pour basculer ; une divergence est un avertissement, et seul le code de raison de la dernière du jour est conservé, jamais une adresse). Jusqu’à la bascule, account flow suspend, une pause pour taux de plainte et la garde de masse sont enregistrés et observés, pas appliqués : la soumission reçoit toujours son 250. La bascule elle-même est prévue, pas servie.

Les pools de sortie

Par défaut, chaque message part de l’adresse par défaut de la box et annonce le nom HELO de la box. Une campagne ne doit pas partager l’adresse d’où les personnes envoient : la réputation se tient par adresse. Un pool de sortie est une adresse (ou une par famille) et son HELO, déclarés dans la configuration parce que c’est un fait de l’hôte :
Une clé inconnue fait refuser le fichier. Chaque adresse locale est liée une fois au démarrage ; une adresse qui n’est sur aucune interface de la machine empêche le démarrage. Un pool s’ajoute avec un redémarrage, et set-pool avertit quand il nomme un pool que la configuration chargée ne contient pas. Jamais de repli. Une ligne dont le pool est absent de la configuration, ou n’a pas d’adresse dans la famille à laquelle la box est figée, attend : aucune tentative n’est comptée et l’expiration ne bouge pas. Un message de masse ne part jamais de l’adresse d’où les personnes envoient. Le pool entre dans chaque clé que le travailleur partage (la session réutilisée, les créneaux par hôte, le disjoncteur d’hôte) : une campagne ne roule donc jamais sur une session ouverte depuis l’adresse par défaut, et le disjoncteur ne bat pas entre deux adresses. Un pool qui a une adresse IPv4 et une adresse IPv6 joint une destination qui n’a que de l’IPv6 depuis son IPv6, et tout le reste depuis son IPv4 : voir sur quelle famille IP le courrier part. Les adresses publiques des pools comptent comme celles de cette box pour la détection de boucle, et leurs adresses IPv4 publiques rejoignent la surveillance de réputation. Un pool dédié au courrier de masse est recommandé, pas exigé : un flux de masse qui n’en a pas partage l’adresse par défaut avec les personnes, et les classes ci-dessous protègent alors la latence, pas la réputation. oximail doctor contrôle chaque pool sous egress_pools : chaque adresse locale est sur une interface, et avec --reachability chaque adresse publique a un DNS inverse confirmé à l’aller sur le HELO et le SPF du HELO la laisse passer.

Les classes de file

Le courrier sortant attend dans trois classes, servies dans cet ordre : humain (human), transactionnel (transactional), masse (bulk). La classe est décidée une seule fois, à la mise en file du message, et stockée sur la ligne de file. Un rapport de livraison (DSN) ou un rapport de relais hérite de la classe de la ligne qui l’a produit. Un compte sans flux qui écrit à plus de 200 destinataires sur une heure glissante est rétrogradé en transactionnel pour la suite, jamais refusé. Le répartiteur de livraison fait tourner 16 créneaux de livraison ([smtp.dispatcher] global_slots, borné par la taille du pool de la base moins 4 ; il n’en faisait tourner que 4 parce qu’il lisait un défaut périmé). La masse n’en tient jamais plus de 12, et les deux derniers sont réservés aux personnes seules (le courrier transactionnel ne les prend pas non plus) ; la masse laisse libre le quart du budget d’octets en vol et le transactionnel un huitième, mais seulement tant qu’autre chose tient des octets ; et des deux créneaux de connexion vers chaque hôte distant, un est gardé pour les personnes. Mesuré sur un banc, le message d’une personne vers un hôte qu’une campagne de 5 000 messages remplissait est parti en 107 ms, pendant que 4 989 messages de masse attendaient encore. oximail doctor rapporte queue_classes : en attente, en envoi et l’âge de la plus ancienne ligne jamais tentée par classe, et il échoue quand le message d’une personne a attendu plus de 60 secondes sans tentative pendant que de la masse part, ce qui veut dire que les budgets ne tiennent pas. Les mêmes chiffres sont oximail_smtp_queue_class_depth{class}, oximail_smtp_queue_class_in_flight{class}, oximail_smtp_queue_class_oldest_unattempted_seconds{class} et la jauge de famine oximail_smtp_queue_human_starved. queue_depth ne fixe de seuil que pour le courrier des personnes et le transactionnel : une campagne est une ligne par destinataire, donc la profondeur de la masse s’affiche, elle ne se juge jamais.

Les exigences des grands fournisseurs

Les grands fournisseurs de messagerie demandent aux expéditeurs de masse un désabonnement en un clic signé, un domaine avec DMARC et un faible taux de plainte. Le relais mesure chacun d’eux et, une fois l’admission appliquée, l’applique. Plaintes. La plainte d’un fournisseur (un rapport ARF, RFC 5965) envoyée à l’adresse de boucle de retour du relais est comptée contre le flux qui a envoyé le message signalé. À vous d’inscrire l’adresse aux boucles de retour des fournisseurs, puis de la nommer :
L’adresse doit exister sur le serveur comme toute adresse de rapports, et le rapport est absorbé sauf si forward est actif. Une plainte n’est crue que si le rapport a passé DMARC pour un fournisseur listé, si l’original signalé porte exactement un X-OxiMail-Flow qui désigne un flux d’origination existant, et si son domaine From: est prouvé par le tenant de ce flux. Tout le reste est journalisé avec sa raison et jamais compté, parce qu’une plainte peut mettre un flux en pause et qu’une plainte forgée est une attaque contre un client. Ce qui est conservé : le flux, le fournisseur, le Feedback-Type et la date, jamais le message signalé ni son destinataire. Gmail n’envoie aucun rapport de plainte : Postmaster Tools lit le Feedback-ID à la place, que la signature couvre. Taux de plainte. Les destinataires distants livrés sont comptés par flux à la fin de la livraison. Chaque heure, le relais juge les sept derniers jours, à partir de 1 000 destinataires livrés et 3 plaintes : 0,1 % déclenche une alerte et 0,3 % met le flux en pause (la raison de la pause nomme le taux). oximail doctor affiche le taux de chaque flux sous complaint_rates, avertit dès le seuil d’alerte et nomme un flux en pause avec le verbe qui la lève, oximail account flow resume --email … --flow … --apply, qui journalise ce qu’il a levé. Les compteurs sont gardés 30 jours. Garde de masse. Pour un flux déclaré de masse, un message doit porter un List-Unsubscribe avec un List-Unsubscribe-Post: List-Unsubscribe=One-Click (RFC 8058), et son domaine From: doit publier DMARC (son propre enregistrement _dmarc, sinon celui de son domaine organisationnel). Des en-têtes manquants ou un domaine sans DMARC sont des refus (5xx). La réponse DMARC est lue dans un cache et jamais interrogée pendant la réception du message : un domaine pas encore connu est différé 451, et le relais le résout en moins d’une minute, le rafraîchit chaque jour, et récupère d’avance chaque domaine qu’un flux de masse peut utiliser (une preuve dkim ou de return-path vérifiée et non suspendue), si bien que le premier message d’un domaine nouvellement prouvé n’attend pas. Signature. Elle couvre la paire de désabonnement, Feedback-ID et X-OxiMail-Flow, comme le demande la RFC 8058 §4 (qui signe quoi). Le taux de plainte est compté et la garde évaluée dès aujourd’hui. Les deux n’agissent sur une soumission que lorsque l’admission est appliquée (voir l’encadré plus haut).

Quand un fournisseur destinataire résiste

Le relais lit ce que répond chaque site destinataire, flux par flux, et peut retenir un flux vers un site pendant que ses autres sites continuent de recevoir. La décision se prend à la remise et tient en deux moitiés, chacune en ombre par défaut (décidée et enregistrée, rien n’est retenu) ou appliquée :
Les pauses. Un flux est suspendu vers un site (le domaine organisationnel du prochain saut, par exemple google.com, outlook.com ou yahoodns.net) pour une heure, doublée si cela recommence dans la journée, six heures au plus. Trois choses déclenchent une pause :
  • une des réponses de limitation de débit que publient Gmail, Microsoft et Yahoo : la première suffit ;
  • 20 refus définitifs qui ne portent pas sur une adresse, en dix minutes ;
  • 3 refus de réputation en une heure (chez Gmail, « likely unsolicited » et « very low reputation »). C’est le déclencheur qu’un flux lent peut atteindre : un seul refus de ce genre ne suspend rien, puisqu’un message peut le recevoir pour son seul contenu.
Un message retenu ne consomme aucune tentative. oximail account flow show liste les pauses d’un flux, et oximail account flow resume --site <site> en lève une. L’allocation journalière. Un pool a un plafond journalier, dont 20 % sont réservés au courrier transactionnel. Chaque domaine de signature, et chaque adresse d’envoi, a une rampe vers chaque site : 500 destinataires par jour au départ, doublés tous les deux jours tant que les refus restent sous 2 %. Ce qui dépasse attend le lendemain, et un message de masse retenu au-delà de sa durée de vie est abandonné, l’abandon nommant l’allocation qui l’a retenu. Sur un serveur qui n’a aucun historique pour un domaine, lisez et réglez ses rampes avant la première grande campagne : oximail account relay ramp list et oximail account relay ramp set. Appliquez pauses d’abord : ce réglage ne retient que ce qu’un site refuse déjà. Appliquez daily une fois qu’une campagne est passée en ombre et que oximail doctor a montré, sous relay_allowance, quelles rampes elle aurait épuisées.

Vérifier

Un 250 que reçoit votre plateforme après DATA signifie que le message survit à une coupure de courant (courrier et SMTP).

Relation avec les comptes relais

Si vous opérez un smarthost, notez la frontière : un compte relais role="service" a sa propre autorisation d’enveloppe (les domaines déclarés prouvés d’ADR-116) et ne peut pas recevoir de grants de domaine d’enveloppe. Un mécanisme par type de compte. Les grants sont pour les comptes normaux d’un Primary normal, qui envoient le courrier de listes de leur propre organisation. Les deux sortes de preuve sont des lignes du domaine du tenant : un tenant ne peut pas détenir une preuve sur le domaine d’un autre tenant.