Délivrabilité

La délivrabilité est le résultat d'une chaîne : authentification entrante, réécriture conditionnelle des en-têtes, signature sortante, gestion des rebonds et des plaintes, et isolation de la réputation. difuzio assure la part applicative ; Postfix et les milters frontaux assurent le reste (voir Installation, section 7).

Authentification entrante

difuzio ne calcule pas SPF/DKIM/DMARC lui-même : il lit les en-têtes Authentication-Results posés par les milters frontaux de confiance. Seuls les en-têtes dont l'authserv-id correspond à celui de l'instance sont pris en compte ; les autres sont ignorés comme potentiellement falsifiés. C'est pourquoi les milters frontaux DOIVENT supprimer tout Authentication-Results préexistant à notre authserv-id à l'entrée (anti-forge).

Les verdicts SPF, DKIM, DMARC et la politique DMARC de l'auteur sont enregistrés sur le message ; ils alimentent les décisions du pipeline (rétention, munging).

Signature DKIM de l'auteur

Une signature DKIM ne survit que si le message n'est pas modifié : le moindre octet réécrit dans le corps, et même un simple réencodage des en-têtes, la rend invalide.

difuzio en tient compte et distingue deux chemins :

  • la liste ne réécrit rien (pas d'étiquette de sujet, pas de pied de page, pas de munging) : le message est transmis octet pour octet. Seuls les en-têtes que difuzio possède sont posés (les List-*, Precedence, Feedback-ID) et les en-têtes de transport de l'entrée sont retirés. La signature de l'auteur reste donc valide, et avec elle son alignement DMARC. C'est le comportement à privilégier pour la délivrabilité ;
  • la liste réécrit le message : la signature de l'auteur est de toute façon cassée, difuzio la retire alors purement et simplement, avec l'Authentication-Results d'entrée. Laisser une signature morte est pire que ne pas en avoir : les destinataires la comptabilisent en dkim=fail.

Concrètement, activer une étiquette de sujet ou un pied de page sur une liste sacrifie la signature de l'auteur. Ce n'est pas grave si le domaine de la liste signe lui-même ses envois en frontal (voir plus bas) ; ce l'est si personne ne signe.

Dans tous les cas, difuzio ne signe pas lui-même : la signature du domaine de la liste est apposée par le MTA frontal, et elle est indispensable pour que le désabonnement en un clic soit honoré par les grandes messageries (RFC 8058).

Réécriture de l'en-tête From (munging)

Quand le domaine de l'auteur publie une politique DMARC stricte, relayer le message tel quel le ferait rejeter par les destinataires (le From ne s'aligne plus après passage par la liste). difuzio réécrit alors l'en-tête From au nom de la liste et retire la signature DKIM de l'auteur (devenue invalide). Le comportement est piloté par la politique from_munging de la liste :

  • auto : munging seulement quand le domaine auteur est en DMARC strict (recommandé) ;
  • always : munging systématique ;
  • never : jamais.

Un échec DMARC entrant sur un domaine auteur en p=reject, pour une liste open ou members, met le post en attente de modération (raison dmarc_inbound, voir Modération).

Désabonnement en un clic (one-click)

Chaque message de liste sortant porte les en-têtes :

List-Unsubscribe: <https://.../p/unsubscribe-oneclick?t=...>, <mailto:...>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

conformément à la RFC 8058. Le lien HTTPS pointe vers POST /p/unsubscribe-oneclick (sans authentification ni CSRF, le token VERP fait foi). Cet endpoint doit être exclu de tout filtrage WAF pour rester fonctionnel (voir Installation).

Rebonds (bounces)

Profil MTA local uniquement. En profil boîte externe, les rapports de non-remise ne reviennent pas à difuzio : cette section, la suivante (plaintes FBL) et le kill-switch de réputation ne s'appliquent pas. Un abonné dont l'adresse cesse de fonctionner se retire à la main depuis la console.

Le chemin de retour de chaque envoi est une adresse VERP signée (bounce+<token>@domaine). À réception d'un rapport de non-remise (DSN), difuzio décode le token, identifie l'abonné et le message, puis applique un score :

  • rebond dur (5.x.x, boîte inexistante par exemple) : l'abonné est désactivé immédiatement (raison bounce) ;
  • rebond mou (4.x.x, temporaire) : le score s'incrémente (environ 20 points par défaut) ; au-delà du seuil bounce.threshold (100) sur la fenêtre bounce.window_days (30 jours), l'abonné est désactivé ;
  • un même DSN re-livré est dédupliqué (hash token + statut + destinataire) : il ne compte pas deux fois.

Les rebonds de courrier de service utilisent le préfixe bounce+svc. et ne sont pas comptabilisés dans le score d'un abonné (seulement journalisés). Un token VERP plus vieux que bounce.verp_max_age_days (90 jours) est rejeté.

difuzio est l'unique rédacteur du score de rebond d'un abonné : le calcul est sérialisé pour éviter les courses entre workers.

Plaintes (FBL / ARF)

Une plainte reçue sur l'adresse de boucle de rétroaction (fbl@domaine, format ARF) désactive immédiatement l'abonné concerné sur la liste (raison complaint), quel que soit son score. Enregistrer fbl@ auprès des programmes FBL (Yahoo CFL, Microsoft JMRP) est indispensable pour recevoir ces signaux.

Réputation et kill-switch

Les rebonds durs et les plaintes alimentent des compteurs de réputation par liste et par jour. Quand le taux de plaintes ou de rebonds durs dépasse les seuils sur la fenêtre, l'envoi de la liste est suspendu automatiquement. Voir Modération, kill-switch.

Pools d'envoi et isolation de la réputation

Le courrier sortant est réinjecté par pool (relay.pools). Chaque pool permet de mapper l'envoi vers une IP source dédiée, pour isoler la réputation entre usages (par exemple transactionnel vs lettres d'information) :

  • selector_mode: header ajoute un en-tête X-Difuzio-Pool à mapper côté Postfix (via header_checks) vers un transport et une IP source ;
  • selector_mode: verp_subdomain utilise un sous-domaine VERP, à router via sender_dependent_default_transport_maps.

Le pool par défaut (relay.default_pool) est obligatoire dès qu'un domaine émet.

Contre-pression adaptative (AIMD)

Comme un 250 du relais signifie "remis au relais" et non "livré", difuzio peut saturer la file Postfix s'il pousse un gros envoi pendant qu'un fournisseur ralentit en aval. Sans lire mailq, le worker observe par pool le ratio deferred/timeout vs sent et applique un contrôleur AIMD : au-delà d'un seuil de dégradation, le débit effectif décroît par moitié ; quand la santé revient, il remonte par paliers vers le plafond nominal (per_pool_messages_per_minute, jamais au-dessus). Chaque changement de régime est journalisé (pool, ancien/nouveau débit, raison). Le contrôleur est par processus : superviser la profondeur de file Postfix (mailq) côté ops reste recommandé.

Enregistrements DNS à publier

Pour chaque domaine émetteur, publier :

  • SPF (v=spf1 ...) autorisant les IP sources des pools ;
  • une clé DKIM par sélecteur signé en frontal ;
  • une politique DMARC (_dmarc, au moins p=none pour commencer, puis durcir).

Vérifier l'ensemble :

difuzio check-dns --domain lists.example.org --dkim-selector default

La commande conclut par un drapeau bulk-sender-ready. Compléter par la surveillance de la file Postfix (mailq) : un 250 du relais signifie "remis au relais", pas "livré au destinataire".