85.1.31. pepsi-stage-vacation

answer mail that arrives while the recipient is away

Section du manuel:

1

85.1.31.1.1. Nom

pepsi-stage-vacation - l’étape d’absence du bureau du pipeline Pepsi.

85.1.31.1.2. Synopsis

pepsi-stage-vacation [GLOBAL-OPTIONS] worker

85.1.31.1.3. Description

pepsi-stage-vacation est un programme d’étape exécuté par pepsi-dispatch(1) comme un worker persistant lisant les identifiants de message sur l’entrée standard. Il charge cette ligne pepsi.workqueue (refusant d’agir sauf si son status est running), lit sa section [stage-<stage>], et — lorsque le destinataire d’enveloppe est absent — envoie l’avis d’absence de ce destinataire à l’expéditeur.

Le message lui-même continue toujours vers NEXT_STAGE. Cette étape ajoute une réponse ; elle ne retient, n’abandonne ni ne fait jamais rebondir de courrier, et NEXT_STAGE est donc obligatoire. Lorsqu’un avis a été envoyé, le Subject: de la copie transmise gagne VACATION_TAG ([VACATION] par défaut) afin que le destinataire voie à son retour à quel courrier on a répondu pour lui.

Le bloc d’en-têtes est chargé, le corps jamais : un avis ne cite rien de l’original.

85.1.31.1.4. L’absence de qui

La configuration est lue à travers la couche de redéfinition par adresse : c’est donc la configuration effective du destinataire d’enveloppe. Les couches, précédence la plus faible d’abord :

[stage-<name>] in pepsi.conf     the operator's default: normally nobody away
  < config_override 'global'       a national holiday, for everybody
  < config_override 'domain:<d>'   a holiday for one served domain
  < config_override 'address:<a>'  a leave the operator enters for someone
  < pepsi.settings[<a>]            the account owner's own leave dates

Rien de tout cela n’est propre à cette étape — pepsi-config(1) et pepsi-settings(1) construisent la même chaîne pour chaque étape — mais c’est l’étape où chaque couche sert : une fonctionnalité par utilisateur dont la valeur par défaut globale est « personne n’est absent » et dont le réglage global est un jour férié. Inscrire l’étape dans les EDITABLE_STAGES de pepsi-stage-edit-settings(1) permet aux propriétaires de comptes de fixer eux-mêmes leurs dates de congé par e-mail.

Lorsque les destinataires d’un message divergent sur les options de cette étape, l’échafaudage l’a déjà scindé en une ligne par configuration distincte avant l’exécution de l’étape. Une ligne peut néanmoins porter encore plusieurs destinataires qui partagent une configuration (deux alias d’une personne, ou tout le monde pendant un jour férié) ; si une absence s’applique, l’étape les sépare aussi, car chaque avis vient d”*une adresse et chaque limitation de débit vaut *pour une adresse.

85.1.31.1.5. Quand un avis est envoyé

La date du jour tombe dans l’un des VACATION_RANGES, et chacune des conditions suivantes est remplie. Chacune est une raison documentée de ne pas répondre, et chacune est journalisée lorsqu’elle se déclenche, de sorte que « pourquoi personne n’a-t-il reçu ma réponse d’absence » trouve sa réponse dans le journal.

  • Le message est entrant. Un message soumis localement (state.local_origin) ne reçoit jamais de réponse — la couche de paramètres indexe un tel message sur son expéditeur, de sorte que sans cela les propres dates de congé du propriétaire du compte répondraient à son propre courrier sortant.

  • Aucune règle de la RFC 3834 n’interdit d’y répondre :

    • l”expéditeur nul <> — un rebond ne reçoit jamais de réponse ;

    • Auto-Submitted: avec toute valeur autre que no (RFC 3834 §5) — le champ existe pour que les répondeurs se reconnaissent entre eux, et chaque avis qu’envoie Pepsi porte auto-replied, ce qui rend sûr de pointer deux instances Pepsi l’une vers l’autre ;

    • Precedence: bulk, list ou junk ;

    • X-Auto-Response-Suppress: nommant All, OOF ou AutoReply ;

    • tout champ List-Id:/List-Post:/List-Unsubscribe:/… , c’est-à-dire que le message est venu par une liste de diffusion ;

    • un expéditeur de service — MAILER-DAEMON, postmaster, noreply, owner-<list>, <list>-request, <list>-bounces+… (formes VERP comprises) — qui n’est la boîte aux lettres de personne et souvent un robot qui répond en retour ;

    • un corps multipart/report, qui est un rapport de remise même lorsqu’il arrive avec un expéditeur d’enveloppe non nul ;

    • le fait que le message vienne de l’adresse répondante elle-même ;

    • un verdict amont state.spam = true : répondre à du spam est du backscatter, et cela confirme une boîte aux lettres vivante à celui qui l’a envoyé.

  • Le destinataire figure dans To:, Cc:, Resent-To: ou Resent-Cc: (RFC 3834 §2/§3 ; Bcc: et Resent-Bcc: sont délibérément exclus), sauf si REQUIRE_ADDRESSED_TO est désactivé. Un courrier qui atteint une boîte aux lettres sans l’y nommer est arrivé par copie carbone invisible, par un alias que l’expéditeur n’a jamais vu, ou depuis une liste moissonnée.

  • Ce correspondant n’a pas déjà reçu de réponse dans les SUPPRESS_DAYS.

L’avis est un message text/plain à expéditeur nul, injecté à RESPONSE_STAGE, où il est signé et relayé comme tout autre courrier sortant. Il est From: l’adresse absente, To: l’expéditeur d’enveloppe, porte Auto-Submitted: auto-replied, et cite le Message-ID: de l’original dans In-Reply-To:/References: afin de se rattacher au fil du message auquel il répond. Son sujet est SUBJECT_PREFIX suivi du sujet original (RFC 5230 §4.5). La copie transmise consigne qu’il y a été répondu sous state.vacation (notified, le recipient, le range correspondant et le language employé) ; voir pepsi.state(7).

Rien n’est envoyé, aucune requête n’est faite et aucun en-tête n’est examiné lorsque VACATION_RANGES est vide, ce qui est la valeur par défaut et l’état de chaque destinataire n’ayant jamais configuré d’absence.

85.1.31.1.6. Contrôle des boucles et des afflux

Un répondeur d’absence est la seule partie d’un système de courrier qui génère un message par message reçu : c’est donc la seule partie qui puisse fabriquer une boucle à partir de rien. Quatre mécanismes indépendants la bornent :

  1. L’expéditeur d’enveloppe nul. Un répondeur distant n’a nulle part où répondre : l’adresse à laquelle il répondrait n’existe pas.

  2. ``Auto-Submitted:``, honoré à l’entrée et posé à la sortie.

  3. La limitation de débit. Même un répondeur distant qui ignore les deux ne reçoit de réponse qu’une fois par SUPPRESS_DAYS : une boucle ne peut donc pas s’emballer — elle coûte un message par semaine.

  4. Le jeton de file. L’avis est injecté sous le jeton <token>-vacation, et cette colonne est UNIQUE : une étape réexécutée après un plantage ne peut donc pas envoyer une seconde copie.

85.1.31.1.7. Intervalles d’absence

VACATION_RANGES est une liste d’intervalles YYYY-MM-DD:YYYY-MM-DD séparés par des virgules ou des blancs. Les deux bornes sont des journées entières incluses, de sorte que 2026-08-01:2026-08-14 couvre entièrement le premier et le quatorze. Deux raccourcis : une fin vide (2026-08-01:) est ouverte, pour un congé sans date de retour fixée, et une date nue (2026-08-01) est ce seul jour.

« Aujourd’hui » est la date courante dans le fuseau horaire du serveur (depuis /etc/localtime) : un opérateur qui écrit 2026-08-14 désigne le quatorze là où il vit, non le quatorze en UTC, ce qui à l’est de Greenwich terminerait le congé pendant l’après-midi précédent. Un déploiement qui veut des bornes UTC tourne avec TZ=UTC, ce qui est une décision sur la machine plutôt qu’une option d’une étape.

La date est résolue au moment où l’étape s’exécute, non à l’arrivée du message : un message retenu deux jours par pepsi-stage-anti-spam(1) ou par une consultation de clé est donc jugé au jour où il atteint finalement cette étape — ce qui est aussi la lecture sûre, puisque l’alternative répond au courrier au nom de quelqu’un déjà de retour à son bureau.

Les intervalles qui se chevauchent sont autorisés ; le premier qui correspond est celui dont l’avis cite les dates. Un intervalle dont la fin précède le début est une erreur de configuration.

85.1.31.1.8. Le message

Le texte de l’avis est un modèle Mustache tenu dans une option INI, non dans un fichier. C’est le seul message de Pepsi que les gens écrivent eux-mêmes, et le seul canal dont dispose un propriétaire de compte — sa ligne pepsi.settings, qu’elle soit posée avec pepsi-settings(1) ou par courrier via pepsi-stage-edit-settings(1) — porte des options de la propre section de cette étape.

Deux sources sont consultées, dans cet ordre, pour chaque langue candidate :

  1. MESSAGE_<LANG> dans la section propre de l’étape (MESSAGE_EN, MESSAGE_DE_CH, …) — redéfinissable par adresse, donc le texte propre de l’utilisateur ;

  2. <LANG> dans la section que nomme DEFAULT_MESSAGE_SECTION (par défaut [pepsi-vacation-default-message], livrée dans ${DATADIR}/config.d afin qu’elle existe sans y toucher). Cette section est lue dans la configuration de base : la couche de réglages par adresse ne peut porter que des options [stage-*], de sorte que seule la forme MESSAGE_<LANG> ci-dessus constitue le texte propre d’un utilisateur.

La première source l’emporte par langue, non en bloc : un utilisateur qui n’écrit que MESSAGE_DE obtient toujours le texte anglais de l’opérateur pour les correspondants anglophones.

85.1.31.1.8.1. Choisir la langue

Les candidates sont les langues détectées de l’expéditeur — state.language, la chaîne de forme Accept-Language que consigne pepsi-stage-detect-language(1) — par confiance décroissante, puis DEFAULT_LANGUAGE. Pour chaque candidate, le message disponible le plus proche est utilisé : l’étiquette exacte, puis sa sous-étiquette primaire (de-ch se rabat sur de), puis toute étiquette qui la raffine (de accepte un DE_CH isolé, de sorte qu’un opérateur n’ayant écrit que du suisse allemand atteigne tout de même les germanophones). Les étiquettes peuvent s’écrire avec l’un ou l’autre séparateur : DE_CH et DE-CH sont la même langue.

Notez que pepsi-stage-detect-language(1) ne rapporte que des sous-étiquettes primaires (en, de), jamais de régions : un message qualifié par région est donc atteint par la règle de raffinement ou en réglant DEFAULT_LANGUAGE dessus.

85.1.31.1.8.2. Champs

{{SENDER_NAME}}

Le nom d’affichage From: de l’expéditeur, ou son adresse complète lorsque le From: ne porte pas de nom.

{{VACATION_START}}

Premier jour d’absence, sous la forme YYYY-MM-DD.

{{VACATION_END}}

Dernier jour d’absence — absent pour un congé ouvert.

{{EMERGENCY_CONTACT}}

Le EMERGENCY_CONTACT configuré — absent lorsqu’il n’y en a pas.

Les deux qui peuvent manquer sont absents, non vides : un modèle branche donc dessus avec {{#VACATION_END}}…{{/VACATION_END}} et {{^VACATION_END}}…{{/VACATION_END}} plutôt que de rendre une phrase trouée. Les dates sont en ISO YYYY-MM-DD : non ambiguës dans toute locale, ce qu’un nom de mois localisé ne serait pas.

L’échappement n’est pas un souci : ces quatre champs se rendent verbatim quel que soit le style d’accolades employé, car l’avis est en text/plain. (Le {{x}} de Mustache échappe normalement le HTML, ce qui saluerait un correspondant nommé O'Brien par O&#39;Brien.)

85.1.31.1.8.3. La règle des deux espaces

Une option INI tient sur une seule ligne : deux espaces deviennent donc un saut de ligne — et quatre espaces deux sauts de ligne, soit un saut de paragraphe. La substitution est appliquée au modèle avant que Mustache ne l’expanse, jamais au résultat, de sorte qu’un nom d’affichage contenant par hasard deux espaces ne puisse pas restructurer le message.

L’analyseur taler rogne la ligne entière : un double espace final est donc perdu à moins que la valeur ne soit entre guillemets (MESSAGE_EN = "…  " — exactement une paire de guillemets encadrants est retirée).

85.1.31.1.9. Options

Toutes ces options sont redéfinissables par adresse.

VACATION_RANGES

Intervalles YYYY-MM-DD:YYYY-MM-DD séparés par des virgules ou des blancs (voir ci-dessus). Vide par défaut, ce qui désactive l’étape pour cette adresse.

RESPONSE_STAGE

Étape à laquelle l’avis est injecté, afin d’être signé et relayé. Requise.

DEFAULT_LANGUAGE

Langue employée lorsqu’aucune des langues détectées de l’expéditeur n’a de message. en par défaut.

DEFAULT_MESSAGE_SECTION

Section de configuration contenant un message en ligne par langue. pepsi-vacation-default-message par défaut.

EMERGENCY_CONTACT

Une unique adresse nue à joindre pendant l’absence du destinataire, offerte au modèle. Non définie par défaut, auquel cas le bloc {{#EMERGENCY_CONTACT}} d’un modèle est sauté.

VACATION_TAG

Ajouté au Subject: du message transmis lorsqu’un avis a été envoyé. [VACATION] par défaut ; la valeur none désactive le marquage. L’ajout est idempotent — un correspondant qui répond à un message marqué recite la marque, et un second passage n’en accumule pas une seconde.

Une valeur vide ne le désactive pas : l’analyseur taler traite une valeur vide comme une option absente, ce qui réapplique la valeur par défaut. C’est pourquoi il existe une sentinelle.

SUPPRESS_DAYS

Combien de temps après avoir répondu à un correspondant celui-ci peut de nouveau recevoir une réponse. 7 par défaut (la valeur par défaut de vacation en Sieve, RFC 5230 §4.1). 0 répond à chaque message ; le maximum est 3650 (dix ans).

REQUIRE_ADDRESSED_TO

Ne répondre que lorsque le destinataire figure dans To:, Cc:, Resent-To: ou Resent-Cc: (RFC 3834 §2/§3). Activé par défaut.

SUBJECT_PREFIX

Ajouté en tête du sujet original pour former le sujet de l’avis. Auto: par défaut (RFC 5230 §4.5). Un message sans sujet donne le seul préfixe, et un sujet déjà préfixé ne l’est pas deux fois.

85.1.31.1.10. Placement

Sur le chemin entrant :

  • après pepsi-stage-detect-language(1), qui fournit state.language, afin que l’avis puisse être dans la langue de l’expéditeur ;

  • après pepsi-stage-check-whitelist(1) et pepsi-stage-anti-spam(1), afin que le spam et le courrier impayé n’attirent aucune réponse ;

  • après pepsi-stage-aliases(1) si des alias sont en usage : la configuration est indexée sur le destinataire d’enveloppe, qui doit donc alors être l’adresse propre de l’utilisateur, non l’alias.

Une conséquence de s’exécuter après pepsi-stage-arc(1) : réécrire le Subject: invalide la signature DKIM de l’auteur et la signature de message ARC que Pepsi a lui-même appliquée, puisque les deux couvrent (et sur-signent) Subject. C’est sans conséquence sur une branche qui se termine par une remise locale — le placement ci-dessus, et celui sur lequel pepsi-stage-decrypt(1) s’appuie déjà pour la même raison. Sur une branche qui relaie le message plus loin, le saut suivant verra ces signatures échouer : réglez-y donc VACATION_TAG = none.

85.1.31.1.11. Base de données

Une table, pepsi.vacation_reply, contenant (recipient, sender, last_sent), et une fonction, vacation_should_reply(), qui décide et consigne dans la même instruction — deux workers tenant deux messages du même correspondant ne trouveraient sinon tous deux aucun avis antérieur et en enverraient tous deux un. Elle élague aussi au passage : les lignes de ce destinataire plus anciennes que sa propre fenêtre, plus un balayage de toute ligne vieille de plus d’un an, pour un destinataire qui cesse de s’absenter ; il n’y a aucune tâche cron à exécuter. Une réservation prise pour un avis qui échoue ensuite à se construire ou à se mettre en file est supprimée à nouveau, de sorte que le correspondant n’est pas marqué comme ayant reçu une réponse sans en avoir reçu. Un message réessayé après la mise en file de son avis n’en envoie pas de second : dans la fenêtre SUPPRESS_DAYS, la réservation le refuse, et avec SUPPRESS_DAYS = 0, le jeton de l’avis, dérivé de celui du message, fait renvoyer par la file l’avis qui s’y trouve déjà.

85.1.31.1.12. Sous-commandes

worker

Exécuté comme un worker persistant de pepsi-dispatch(1), lisant les identifiants de message sur l’entrée standard.

85.1.31.1.13. Options globales

-c FILE, –config FILE

Lit la configuration depuis FILE au lieu de parcourir les emplacements par défaut.

-L LOGLEVEL, –log LOGLEVEL

Règle la verbosité de journalisation (par défaut info).

-v, –verbose

Affiche les messages de journal de toutes les sources.

-h, –help ; -V, –version

Affiche un résumé d’utilisation / la version et quitte.

85.1.31.1.14. Code de sortie

0

Le message a été traité (transmis, qu’un avis ait été envoyé ou non).

1

Une erreur s’est produite (message introuvable ou non running, ou un réglage par adresse qui rend la section de l’étape invalide — un intervalle de dates erroné, par exemple). La raison est écrite dans le journal.

Une défaillance de l’hôte (la base de données, un modèle ou un helper inutilisable) n’est pas signalée comme un échec : le message est mis en pause et réessayé, comme le décrit Erreurs d’étape dans pepsi-dispatch(1). Une section qui ne s’analyse pas fait refuser au worker de démarrer (statut 78) au lieu de mettre chaque message en échec à tour de rôle.

Un avis qui ne peut être construit ou mis en file n’est pas une erreur : il est journalisé comme avertissement et le message est transmis inchangé. Le courrier du destinataire compte plus que la réponse de courtoisie à l’expéditeur.

85.1.31.1.15. Exemples

Répondre à un message (il doit être running)

echo 42 | pepsi-stage-vacation -c /etc/pepsi/pepsi.conf worker

Une section de pipeline sur le chemin entrant, sans absence configurée — l’opérateur câble l’étape une fois et les utilisateurs l’activent pour eux-mêmes

[stage-vacation]
PROGRAM = pepsi-stage-vacation
NEXT_STAGE = local
RESPONSE_STAGE = dkim-sign

Un jour férié national, pour chaque adresse que l’instance sert

pepsi-config -c /etc/pepsi/pepsi.conf set --scope global \
    stage-vacation VACATION_RANGES 2026-12-24:2026-12-26

Le congé d’un utilisateur, saisi par l’opérateur

pepsi-settings -c /etc/pepsi/pepsi.conf \
    set alice@example.org vacation VACATION_RANGES 2026-08-01:2026-08-14
pepsi-settings -c /etc/pepsi/pepsi.conf \
    set alice@example.org vacation EMERGENCY_CONTACT ops@example.org

…ou par l’utilisateur lui-même, si l’opérateur a inscrit vacation dans les EDITABLE_STAGES de pepsi-stage-edit-settings(1) — un courrier à pepsi@example.org avec le sujet Pepsi et ce corps

[stage-vacation]
VACATION_RANGES = 2026-08-01:2026-08-14
MESSAGE_EN = Hi {{SENDER_NAME}},  I am off sailing until {{VACATION_END}}.

85.1.31.1.16. Voir aussi

pepsi-config(1), pepsi-settings(1), pepsi-stage-detect-language(1), pepsi-stage-edit-settings(1), pepsi-stage-aliases(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)

RFC 3834 (réponses automatiques), RFC 5230 §4 (l’action Sieve vacation, dont cette étape suit les règles :days et de sujet).

85.1.31.1.17. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.