46. pepsi-stage-vacation

Answer mail that arrives while the recipient is away.

46.1. Role

pepsi-stage-vacation is the out-of-office responder. When the envelope recipient is away on the day the stage runs, it sends that recipient’s vacation notice to the sender and tags the forwarded copy’s Subject:. The message itself always continues to NEXT_STAGE: the stage adds a reply, it never holds, drops or bounces mail. Reference: pepsi-stage-vacation(1).

46.2. Whose vacation

The stage reads its options through the per-address override layer, so they are the envelope recipient’s. That is what makes one stage serve both cases the feature has to cover:

[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

The chain is not this stage’s invention — Configuration describes it, and every stage gets it — but this is the stage that uses all of it. Operators enter a holiday with pepsi-config, a user’s leave with pepsi-settings; listing vacation in pepsi-stage-edit-settings’s EDITABLE_STAGES lets account owners set their own dates by e-mail.

A row that still carries several recipients sharing one configuration (two aliases of a person, or everybody during a holiday) is split one recipient per row before any notice is built: each notice is from one address, and each rate limit is for one address.

46.3. When a notice is sent

Today — in the server’s time zone, resolved when the stage runs — falls in one of the VACATION_RANGES (inclusive whole days; 2026-08-01: is an open-ended leave), and the message is one it is proper to answer. The full RFC 3834 rule set is in the man page; the short version is that Pepsi does not answer bounces, mailing-list mail, anything marked Auto-Submitted: or Precedence: bulk, service senders such as MAILER-DAEMON or <list>-request, delivery reports, mail an upstream stage marked as spam, mail the recipient sent themselves, or mail that does not name the recipient in To:/Cc:. On top of that, one correspondent is answered at most once per SUPPRESS_DAYS (default 7).

An out-of-office responder is the one component that emits a message per received message, so four things bound it at once: the notice’s null envelope sender (nothing can reply to it), the Auto-Submitted: auto-replied it carries, the per-correspondent rate limit (a responder that ignores both still costs one message a week), and the UNIQUE queue token that makes a retried stage unable to send a second copy.

46.4. The message

The notice text is a Mustache template held in an INI option rather than in a TEMPLATE_DIR file, because this is the message users write themselves and the settings layer carries options, not files. Per candidate language the stage prefers MESSAGE_<LANG> in the stage’s own section (the user’s own text) and falls back to <LANG> in DEFAULT_MESSAGE_SECTION — by default [pepsi-vacation-default-message], which ships in ${DATADIR}/config.d with ten languages, so a working default exists before anyone configures anything.

The language is chosen from state.language (written by pepsi-stage-detect-language) and falls back to DEFAULT_LANGUAGE. The fields are {{SENDER_NAME}}, {{VACATION_START}}, {{VACATION_END}} and {{EMERGENCY_CONTACT}}; the last two are absent rather than empty when they do not apply, so a template can branch. Since an INI option is one line, two spaces become a newline (four, a paragraph break).

46.5. Placement

After pepsi-stage-detect-language (for the language), after pepsi-stage-check-whitelist and pepsi-stage-anti-spam (so spam and unpaid mail draw no reply), and after pepsi-stage-aliases if aliases are in use (the configuration is keyed on the envelope recipient, which must be the user’s own address by then).

Rewriting the Subject: invalidates the originator’s DKIM signature and Pepsi’s own ARC message signature, both of which cover it — harmless on a branch ending in local delivery, which is the placement above and the one pepsi-stage-decrypt already relies on. On a branch that relays the message onward, set VACATION_TAG = none.

46.6. Configuration

[stage-<name>]: PROGRAM = pepsi-stage-vacation, NEXT_STAGE (mandatory), RESPONSE_STAGE (mandatory — where the notice is injected to be signed and relayed), VACATION_RANGES, DEFAULT_LANGUAGE, DEFAULT_MESSAGE_SECTION, EMERGENCY_CONTACT, VACATION_TAG, SUPPRESS_DAYS, REQUIRE_ADDRESSED_TO and SUBJECT_PREFIX. pepsi-setup rejects a bad date range, an unresolvable NEXT_STAGE/RESPONSE_STAGE, and a configuration with no message for DEFAULT_LANGUAGE. See pepsi-stage-vacation(1) and Configuration.

46.7. State

  • Inputs: state.language (for the notice’s language), state.local_origin (a locally submitted message is never answered), state.spam.

  • Outputs: state.vacation recording that a notice went out, for whom, for which range and in which language — so pepsi-stage-if can branch on it and pepsi-queue shows it. Nothing else in state is touched, and state.dsn is preserved.

  • Transitions: always advance to NEXT_STAGE; a separate null-sender notice is injected at RESPONSE_STAGE.

46.8. Database

pepsi.vacation_reply (one row per answered correspondent) and vacation_should_reply(), which claims and records the right to answer in one statement so two workers cannot both answer the same correspondent. Aged rows are pruned in passing; there is no job to schedule.

46.9. See also

pepsi-stage-detect-language, pepsi-stage-edit-settings, pepsi-settings, pepsi-config, Supported Features, pepsi-stage-vacation(1).