70.1.9. pepsi-stage-bounce

rewrite a message into a delivery-status bounce

Manual section:

1

70.1.9.1.1. Name

pepsi-stage-bounce - the bounce-generation stage of the Pepsi pipeline.

70.1.9.1.2. Synopsis

pepsi-stage-bounce [GLOBAL-OPTIONS] worker

70.1.9.1.3. Description

pepsi-stage-bounce is a stage program: it is run by pepsi-dispatch(1) as a persistent worker, reading message ids on standard input, each identifying a row of the pepsi.ingress table. It loads that row, refusing to act unless its status is running, and reads its own configuration from the message’s [stage-<stage>] section (see pepsi.conf(5)).

It rewrites the message in place into an RFC 3464 delivery-status notification (DSN): the envelope sender becomes the null sender (<>), the envelope recipient becomes the original message’s sender, and the body becomes a multipart/report quoting the original headers.

“The original message’s sender” is state.srs.original when pepsi-stage-srs(1) recorded one, and the mail_from column otherwise. The distinction matters on any relaying path, because SRS necessarily runs before the delivery stage: by the time a DSN is composed here the envelope sender is one of our own SRS aliases, and addressing the report to it would send it out to the next hop and back in through our own MX to be decoded — surviving the whole inbound pipeline as a null-sender message — only to reach an address that was on the row all along. SRS is a return path for a bounce the next hop generates; a report we write ourselves needs no such detour. See pepsi.state(7). By default this is a failure bounce (Action: failed); when the routing stage tags state.bounce with kind = success it is instead a positive (Action: delivered) report. The diagnostic and recipient are taken from the state the routing stage left on the row. The DSN’s From: is the POSTMASTER address. The rewritten DSN is left unsigned; the row is advanced to the stage’s NEXT_STAGE, normally pepsi-stage-dkim-sign(1), which DKIM-signs it (as the postmaster’s domain) before a delivery stage such as pepsi-stage-relay-to-internet(1) relays it.

A message that is already a bounce (it has the null envelope sender) is not rewritten: it is deleted and the deletion is logged. A bounce is never itself bounced (RFC 5321 §6.1).

DSN (RFC 3461): a failure bounce is generated only when the failed recipient’s NOTIFY requested FAILURE. The NOTIFY/ORCPT/ENVID the stage acts on are the ones the routing delivery stage copied onto the state.bounce object (from the recipient’s state.dsn entry). The default — no NOTIFY carried — is FAILURE, so ordinary mail still bounces; but NOTIFY=NEVER (or a NOTIFY list without FAILURE) means the sender does not want a failure report, and the message is then dropped without a bounce. When the original ENVID and ORCPT were supplied they are echoed into the DSN as Original-Envelope-Id and Original-Recipient.

A success report (kind = success) is generated only when the recipient explicitly requested NOTIFY=SUCCESS (success, unlike failure, has no implicit default); the routing stage emits one solely when the global [pepsi] ORIGINATE_SUCCESS_DSN is enabled. A delay report (kind = delay, Action: delayed) is generated only when the recipient requested NOTIFY=DELAY; a delivery stage enqueues a copy of a still-undelivered message here (see DELAY_DSN_AFTER in pepsi-stage-relay-to-internet(1)) while the original keeps being retried. Both success and delay have no implicit default.

The human-readable (text/plain) part of the bounce is, by default, a built-in English notice. When the stage’s BOUNCE_MESSAGE option names a template, that part is instead rendered from bounce-<NAME>.<lang>.body under the [pepsi] TEMPLATE_DIR (a Mustache template; see pepsi.conf(5)). The whole message state is the rendering context, so a delivery stage’s captured next-hop detail (state.bounce.remote_mta / smtp_code / enhanced_status / phase / reply_text) lets the bounce state precisely why the next MTA refused the message. Rendering is best-effort: any failure falls back to the built-in text, so a bounce is always produced.

pepsi-stage-bounce performs no network delivery and issues no notification; the rewritten message is delivered by whichever stage NEXT_STAGE names.

70.1.9.1.4. Configuration

The bounce stage’s options (SERVER_NAME, POSTMASTER, the required NEXT_STAGE and the optional BOUNCE_MESSAGE template) are documented in pepsi.conf(5).

70.1.9.1.5. State

Inputs: state.bounce — the report context the routing stage left on the row. Its kind (permanent ⇒ a failure Action: failed report, successAction: delivered, delayAction: delayed) selects the report type; diagnostic and failed_recipient are quoted in it; the optional notify/orcpt/envid decide whether the report is emitted at all and what is echoed; and the optional structured next-hop fields remote_mta/smtp_code/enhanced_status/phase/reply_text (written by the relay stages) are surfaced to the BOUNCE_MESSAGE template and, for enhanced_status, into the DSN Status: field. A hand-staged message with no state.bounce yields a generic failure notice.

Outputs: the stage rewrites the message in place and clears ``state`` to null — the bounce is a new null-sender message that inherits none of the original’s provenance (this is the one stage that does not preserve state). The state layout is described in pepsi.state(7).

Transitions:

  • an incoming message that is itself a bounce (null sender) → finish (dropped, never re-bounced);

  • the recipient’s NOTIFY does not request the report this kind would emit (failure has an implicit default; success/delay do not) → finish (dropped without a DSN);

  • otherwise the message is rewritten in place into a DSN and advanced to NEXT_STAGE (the signing/delivery tail).

The stage never pauses, fails or reroutes.

70.1.9.1.6. Commands

worker

Run as a persistent pepsi-dispatch(1) worker, reading message ids on standard input.

70.1.9.1.7. Global Options

These global options precede the subcommand.

-c FILE, –config FILE

Read the configuration from FILE instead of searching the default locations.

-L LOGLEVEL, –log LOGLEVEL

Set the logging verbosity (error, warn, info, debug or trace; default info).

-v, –verbose

Show log messages from all sources, including third-party libraries.

-h, –help

Print a usage summary and exit.

-V, –version

Print the version and exit.

70.1.9.1.8. Exit Status

0

The message was processed (rewritten and advanced, or dropped).

1

An error occurred: the message was not found or not running, the stage was misconfigured, or a database error occurred. The reason is written to the log; pepsi-dispatch(1) marks a row still running after a non-zero exit as failed.

70.1.9.1.9. Examples

Run the bounce stage on message 42 by hand (it must be running):

pepsi-stage-bounce -c /etc/pepsi/pepsi.conf 42

70.1.9.1.10. See Also

pepsi-config(1), pepsi.conf(5), pepsi.state(7), pepsi-stage-dkim-sign(1), pepsi-dispatch(1), pepsi-stage-relay-to-internet(1), pepsi-stage-relay-to-smarthost(1), pepsi-setup(1)

70.1.9.1.11. Bugs

Report bugs to the Pepsi issue tracker.