---
title: "Comptes de messagerie externes"
weight: 12
description: "Le profil boîte externe de bout en bout : relève IMAP, submission authentifié, comptes par liste saisis dans la console, limites assumées et verrou global."
---

# Comptes de messagerie externes

Ce profil fait tourner difuzio **sans serveur de messagerie à soi**. difuzio
relève une boîte mail ordinaire chez un hébergeur en IMAP, et renvoie les
messages aux abonnés par le submission SMTP authentifié du même hébergeur. Aucun
enregistrement MX à changer, aucun Postfix à configurer : une boîte et ses
identifiants suffisent.

    Internet -> boîte hébergeur -> poller IMAP (boucle du worker) -> pipeline -> submission 587 AUTH -> abonnés

C'est l'un des deux profils décrits dans
[Architecture](/difuzio/admin/architecture) ; l'autre suppose un Postfix en
frontal. Le coeur de difuzio est identique dans les deux cas : seules changent
l'entrée et la sortie du courrier.

Cette page se lit de bout en bout : elle contient tout ce qui est nécessaire à
l'installation dans ce profil. Les sections Postfix, MX et rebonds VERP des
autres pages ne s'appliquent pas.

## À qui ce profil s'adresse

Une petite structure - typiquement une association de quelques dizaines de
membres - qui possède une adresse chez un hébergeur mutualisé ou une offre mail
grand public, et aucun accès administrateur à un serveur de messagerie.

Il se marie naturellement avec le backend SQLite : SQLite supprime la dépendance
à un serveur de base de données, ce profil supprime la dépendance à un MTA. Les
deux ensemble donnent une installation autonome : un binaire, un fichier `.db`,
une boîte mail.

Bénéfice de délivrabilité souvent sous-estimé : le courrier part réellement des
serveurs de l'hébergeur, pour une adresse de son domaine, via son submission
authentifié. SPF, DKIM et DMARC sont donc alignés nativement, sans publication
DNS supplémentaire. Ce profil est fréquemment meilleur qu'un relais maison mal
configuré. Le facteur limitant est le débit, pas la réputation.

## Limites assumées

À connaître avant de promettre quoi que ce soit :

- **Petites listes seulement.** Le débit est plafonné par le quota d'envoi de la
  boîte, souvent quelques centaines de messages par jour. Un message vers 50
  abonnés, ce sont 50 envois. Au-delà de cet ordre de grandeur, il faut un vrai
  sortant dédié.
- **Abonnements par le web uniquement.** Pas de commandes par courriel
  (`subscribe@`, `unsubscribe@`, `owner@`). L'abonnement et le désabonnement
  passent par les pages web et par le lien de désabonnement en un clic présent
  dans chaque message. En IMAP, l'enveloppe SMTP est perdue : il n'y a plus
  d'adresse de destination à classer, donc plus d'adresses de commande.
- **Pas de traitement automatique des rebonds.** Un abonné dont l'adresse cesse
  de fonctionner n'est pas désactivé tout seul ; le retirer à la main depuis la
  console.
- **Mono-hôte.** Un seul `worker` relève la boîte. Ne pas en lancer deux sur la
  même boîte.

## Une boîte = une liste

C'est la règle qui rend le profil simple et sûr. Tout message présent dans la
boîte `membres@lists.example.org` est, par définition, un message destiné à la
liste `membres`. Il n'y a rien à deviner.

Conséquence directe : ne jamais abonner la boîte de la liste à sa propre liste,
sinon chaque message reboucle. difuzio refuse cet abonnement, mais ne pas
l'ajouter à la main non plus.

## Prérequis chez l'hébergeur

- Une boîte dédiée par liste, par exemple `membres@lists.example.org`.
- IMAP accessible : IMAPS (993) ou IMAP + STARTTLS (143).
- Submission SMTP authentifié : 587 avec STARTTLS et AUTH.
- Un **mot de passe applicatif** si l'offre en exige un (fréquent).
- Le **quota d'envoi** de l'offre, pour régler `messages_per_minute`.

## 1. Binaire et base

Le profil va de pair avec la variante SQLite :

    go build -tags sqlite -o difuzio ./cmd/difuzio
    difuzio migrate up --dsn /var/lib/difuzio/difuzio.db

La variante MariaDB fonctionne aussi ; seul le DSN change. Voir
[Base de données](/difuzio/admin/base-de-donnees).

## 2. Secrets

Tous les secrets vivent dans des fichiers 0600, jamais en base, jamais en clair
dans le YAML :

    install -d -m 0700 /etc/difuzio/secrets

    printf '%s' 'MOT_DE_PASSE_IMAP' > /etc/difuzio/secrets/imap-membres
    printf '%s' 'MOT_DE_PASSE_SMTP' > /etc/difuzio/secrets/smtp-membres
    chmod 0600 /etc/difuzio/secrets/imap-membres /etc/difuzio/secrets/smtp-membres

    for k in session token verp credential; do
      openssl rand -base64 32 > /etc/difuzio/secrets/$k.key
      chmod 0600 /etc/difuzio/secrets/$k.key
    done

Le démarrage échoue si un fichier de secret est lisible par le groupe ou par les
autres. Les trois premiers keyrings sont obligatoires ; `credential.key` n'est
nécessaire que pour saisir les comptes dans la console (section 4), ce qui est le
mode recommandé.

## 3. Configuration

Partir de `config.example-external.yaml`. Les points spécifiques à ce profil :

    relay:
      host: smtp.example.net
      port: 587
      starttls: true                # obligatoire dès qu'un bloc auth existe
      messages_per_minute: 30       # rester sous le quota de l'hébergeur
      auth:
        mechanism: plain
        username: "robot@lists.example.org"
        password_file: "/etc/difuzio/secrets/smtp-robot"

    ingest:
      enabled: true
      poll_interval: 1m
      max_fetch: 50
      mark_processed: seen          # seen | move (move exige processed_folder)
      mailboxes:
        - server: imap.example.net
          tls: implicit             # implicit (993) | starttls (143)
          port: 993
          username: "membres@lists.example.org"
          password_file: "/etc/difuzio/secrets/imap-membres"
          mailbox: INBOX
          domain: lists.example.org # doit correspondre à un domains[].name
          list: membres

La configuration est validée une seule fois au démarrage : une clé manquante ou
une incohérence (mot de passe sans STARTTLS, domaine inconnu, fichier de secret
absent) fait échouer le boot en nommant la clé fautive. Détail de chaque section
dans [Configuration](/difuzio/admin/configuration).

### Hébergeurs qui exigent AUTH = From (OVH notamment)

Certains submissions n'acceptent un message que si son expéditeur correspond au
compte authentifié : le `From` doit être une adresse du compte (ou un alias), et
l'enveloppe `MAIL FROM` doit elle aussi lui appartenir - ce qui exclut l'adresse
de retour VERP employée par défaut. Deux réglages couvrent ce cas :

    relay:
      auth:                         # identité de repli : le robot du domaine
        mechanism: plain
        username: "robot@lists.example.org"
        password_file: "/etc/difuzio/secrets/smtp-robot"
      auths:                        # un compte d'envoi par liste
        membres@lists.example.org:
          mechanism: plain
          username: "membres@lists.example.org"
          password_file: "/etc/difuzio/secrets/smtp-membres"
      envelope_from: auth           # MAIL FROM = compte authentifié

Chaque liste citée dans `relay.auths` s'authentifie avec son propre compte : le
`From` (l'adresse de la liste, garantie par le munging DMARC) et l'identité AUTH
coïncident. Tout le reste - courriers de service émis par le robot, listes sans
compte dédié - retombe sur `relay.auth`, qui devient obligatoire dès que
`relay.auths` existe.

Alternative sans multi-comptes : si l'offre le permet, déclarer les adresses de
listes comme alias d'un unique compte d'envoi et ne garder que `relay.auth` avec
`envelope_from: auth`. À tester : le contrôle exact varie d'une offre à l'autre.

Limite connue : le mode digest émet avec un `From` en sous-adressage
(`liste+digest@domaine`), que les offres sans sous-adressage peuvent refuser.
Créer l'alias si possible, sinon laisser les abonnés concernés en mode `regular`.

## 4. Saisir les comptes dans la console (recommandé)

Les blocs `relay.auths` et `ingest.mailboxes` ci-dessus imposent un accès root au
serveur et un redémarrage à chaque liste ajoutée. Les mêmes identifiants se
saisissent dans la console, liste par liste, dans la section "Comptes de
messagerie du fournisseur" de la fiche de liste. Le bouton "Tester la connexion"
ouvre une vraie session (connexion, TLS, authentification, ouverture du dossier
pour l'IMAP) sans envoyer ni relever le moindre message, et affiche le refus de
l'hébergeur mot pour mot.

Ce mode demande le keyring `security.credential_keyring` :

    security:
      credential_keyring:
        current: 1
        keys:
          - { version: 1, file: "/etc/difuzio/secrets/credential.key" }

Points à connaître :

- **Le mot de passe n'est jamais réaffiché.** Il est chiffré à l'enregistrement
  et aucune réponse de l'API ne le renvoie. Sur une modification, laisser le
  champ vide conserve le mot de passe stocké : corriger un port ou un dossier ne
  demande pas de l'avoir sous la main.
- **La clé est indispensable et non reconstructible.** La perdre rend les mots de
  passe illisibles (la console affiche "Illisible" liste par liste) et il faut
  les ressaisir. La sauvegarder avec les trois autres.
- **Sans ce keyring, rien ne change** : l'installation démarre à l'identique et la
  console refuse d'enregistrer un compte en nommant la clé manquante, plutôt que
  d'écrire un mot de passe en clair.
- **La base prime sur le fichier.** Une liste ayant un compte en base et une
  entrée YAML utilise celui de la base ; l'entrée ignorée est nommée dans le
  journal du worker. Une fois tout saisi, retirer `relay.auths` et
  `ingest.mailboxes` du fichier. `relay.auth` reste nécessaire.
- **Prise en compte sans redémarrage** : l'envoi suit dans la demi-minute, la
  relève au cycle suivant.
- **Qui peut le faire** : un administrateur de domaine ou un super_admin. Le
  propriétaire de la liste voit l'état du compte sans pouvoir le modifier : le
  serveur d'où part le courrier est une décision d'infrastructure.
- **Anti-SSRF** : un hôte saisi dans la console doit résoudre vers une adresse
  publique. Un serveur sur adresse privée reste possible par le YAML, que seul
  l'exploitant peut éditer.

Reprendre des identifiants déjà présents dans le fichier, sans les ressaisir :

    difuzio credential import --config /etc/difuzio/config.yaml
    difuzio credential import --config /etc/difuzio/config.yaml --apply

Sans `--apply`, la commande affiche ce qu'elle ferait et n'écrit rien. Voir
[Référence CLI](/difuzio/admin/cli-reference), groupe `credential`.

## 5. Interdire ce profil sur un serveur

Un exploitant qui ne veut pas de comptes de fournisseur sur son installation
ferme le profil pour tout le monde :

    policy:
      external_mailboxes: deny

Aucun administrateur de domaine ne peut alors enregistrer de compte IMAP/SMTP :
la console n'affiche plus la section, l'API répond `403
external_mailboxes_disabled`, et `ingest.enabled: true` comme `relay.auths`
deviennent des erreurs de configuration au démarrage. Les détails, notamment le
comportement quand des comptes existent déjà, sont dans
[Configuration](/difuzio/admin/configuration), section `policy`.

## 6. Démarrage

Deux processus seulement : pas de `serve-lmtp`, il n'y a pas de MTA en frontal.

    difuzio serve-web --config /etc/difuzio/config.yaml
    difuzio worker    --config /etc/difuzio/config.yaml

Le `worker` héberge le poller IMAP : il relève la boîte à l'intervalle
`ingest.poll_interval`, ingère les messages dans le pipeline, puis les marque
traités (`\Seen`, ou déplacement si `mark_processed: move`).

## 7. Créer la liste et inscrire les membres

1. Créer le domaine et la liste, activer le domaine (voir
   [Domaines](/difuzio/admin/domaines) et [Listes](/difuzio/admin/listes) ; la
   section "Intégration Postfix" ne concerne pas ce profil).
2. **Déclarer que les adresses de service ne sont pas routées** :

       difuzio domain set-command-addresses --domain lists.example.org --routed=false

   Étape à ne pas sauter. Sans elle, chaque message annonce dans ses en-têtes
   `List-Help`, `List-Subscribe`, `List-Owner` et `List-Unsubscribe` des adresses
   (`liste-subscribe@`, `liste-owner@`...) que l'hébergeur ne connaît pas :
   l'abonné qui les utilise reçoit un rapport de non-remise. Avec le réglage, ces
   en-têtes ne portent plus que les URL web, qui fonctionnent. Le même
   interrupteur existe dans la console, sur la fiche du domaine, section "Routage
   du courrier entrant".
3. Inscrire les membres par le formulaire web (double opt-in) ou par un import.
4. Ne pas inscrire l'adresse de la boîte elle-même.

## 8. Vérification

- `difuzio doctor --config /etc/difuzio/config.yaml` confronte le fichier à la
  base avant même le premier démarrage : il échoue si une boîte vise un domaine
  ou une liste absent ou inactif, et affiche la commande qui corrige.
- Au démarrage du worker, le poller refait le contrôle : "imap preflight ok", ou
  une ligne par boîte fautive avec sa raison et son remède. Le worker ne s'arrête
  pas : créer le domaine ou la liste manquante est pris en compte au cycle
  suivant.
- Envoyer un message de test à l'adresse de la liste depuis une adresse abonnée :
  il doit être redistribué à tous les abonnés.
- Vérifier l'alignement SPF/DKIM/DMARC dans un message reçu.

## Dépannage

- **"relay.starttls must be true when relay.auth is set"** : activer
  `relay.starttls: true`. difuzio n'envoie jamais d'identifiants en clair.
- **"is group/other accessible"** : un fichier de secret n'est pas en 0600.
- **"is not a configured domain"** : `ingest.mailboxes[].domain` ne correspond à
  aucun `domains[].name`.
- **`resolve domain "X": repo: not found`** : le domaine est déclaré dans le
  fichier mais absent de la base. Aucun message n'est perdu pendant la panne : ils
  restent non lus dans la boîte et sont relevés après correction.

      difuzio add-domain --name X --base-url https://<vhost> --config /etc/difuzio/config.yaml
      difuzio set-domain-status --domain X --status active --config /etc/difuzio/config.yaml
      difuzio create-list --domain X --name <liste> --owner <email> --config /etc/difuzio/config.yaml

- **AUTH refusé à l'envoi** : vérifier les identifiants (mot de passe applicatif
  éventuel), le port et STARTTLS.
- **"relay.auths requires relay.auth"** : `relay.auths` exige l'identité de repli
  `relay.auth`.
- **5xx "sender not allowed"** : l'hébergeur refuse une enveloppe ou un `From`
  étrangers au compte. Passer `envelope_from: auth` et donner son compte à chaque
  liste.
- **Rien n'est relevé** : vérifier IMAP (hôte, port, TLS, identifiants), que la
  liste cible est active, et que des messages non lus sont présents.
- **Un abonné ne reçoit plus rien** : son adresse a peut-être cessé de
  fonctionner. Il n'y a pas de purge automatique dans ce profil : le retirer à la
  main.
- **"security.credential_keyring is required"** : le keyring de chiffrement n'est
  pas déclaré, voir la section 4.
- **"the database holds N mailbox credential(s) but security.credential_keyring is
  not configured"** au démarrage : des comptes ont été saisis puis le keyring a
  disparu de la configuration. Le démarrage est volontairement fatal : sans la
  clé, ces listes seraient muettes sans que rien ne le dise. Remettre la clé, ou
  supprimer les comptes.
- **"policy.external_mailboxes is deny but the database still holds ..."** : la
  politique a été fermée alors que des comptes subsistent. Les supprimer avec
  `difuzio credential rm`, ou rouvrir la politique.
- **Un compte marqué "Illisible"** : le mot de passe a été scellé avec une version
  de clé que l'installation n'a plus. Le ressaisir rétablit la liste.
- **Un abonné dit avoir écrit à `liste-unsubscribe@` sans réponse** : l'étape 2 de
  la section 7 n'a pas été faite. Le domaine annonce encore des adresses que rien
  ne dessert ; passer `--routed=false` et les messages suivants n'annonceront plus
  que les liens web.
