70.1.14. pepsi-stage-relay-to-lmtp

deliver a message to a local MDA (Dovecot) over LMTP

Manual section:

1

70.1.14.1.1. Name

pepsi-stage-relay-to-lmtp - the LMTP local-delivery stage of the Pepsi pipeline.

70.1.14.1.2. Synopsis

pepsi-stage-relay-to-lmtp [GLOBAL-OPTIONS] worker

70.1.14.1.3. Description

pepsi-stage-relay-to-lmtp is a stage program run by pepsi-dispatch(1). It loads the pepsi.ingress row (refusing to act unless its status is running), reads its [stage-<stage>] section, and hands the message to a Mail Delivery Agent — typically Dovecot — over LMTP (RFC 2033) for final local delivery. The MDA applies each recipient’s Sieve (RFC 5228) script as it files the mail, so server-side filtering (fileinto, vacation, redirect, flag setting, …) is provided by the MDA rather than by Pepsi.

The stage is LMTP-native: every envelope recipient is offered in a single LMTP transaction and the MDA returns a separate reply per recipient after end-of-DATA. The stage itself never builds a delivery-status notification — it delivers, and otherwise routes each recipient onward to a configured stage, recording why under state.bounce so a downstream pepsi-stage-bounce(1) (or a relay) can act on it. Each recipient’s fate follows its reply:

  • 2xx — delivered; the recipient leaves the row. When the recipient asked for NOTIFY=SUCCESS and [pepsi] ORIGINATE_SUCCESS_DSN is set, a positive notification is routed to the optional NOTIFY_STAGE.

  • a permanent reject is routed by reason, classified from the reply’s RFC 3463 enhanced status: a bad destination mailbox (x.1.x, or a bare 550 user-unknown) goes to NEXT_STAGE; an over-quota reject (x.2.2) to QUOTA_LIMIT_STAGE (else NEXT_STAGE); a Sieve / local policy reject (x.7.x) to SIEVE_REJECT_STAGE (else NEXT_STAGE); anything else to NEXT_STAGE.

  • 4xx (e.g. server busy) — a transient failure: the recipient is retried with exponential backoff until MAX_LIFETIME, then routed by the same reason classification.

A message can therefore be partially handled: delivered, routed and deferred recipients are reconciled in one fan-out — the delivered recipients leave the row, each routed recipient becomes a sibling pending row (sliced to that one recipient, so a relay target never re-delivers to its siblings) at its target stage, and the original row is reduced to just the still-deferred recipients for the next retry. A DELAY warning (when due and NOTIFY=DELAY was requested) is likewise routed to NOTIFY_STAGE.

Because the MDA decides which addresses are local, this stage performs no locality test of its own (unlike pepsi-stage-relay-to-maildir(1)). Place it where only intended-local recipients arrive — usually after alias expansion (pepsi-stage-aliases(1)). It is not terminal: NEXT_STAGE is mandatory — set it to a pepsi-stage-bounce(1) to bounce undeliverable recipients, or to a relay stage to forward them off-site. A message that reaches the stage with no NEXT_STAGE configured is marked failed.

Unlike local Maildir writing, LMTP needs no privileged helper: the MDA performs the per-user delivery itself, so this stage runs as the unprivileged pepsi user and carries no setuid/setgid bit. It is folded into the unified pepsi binary.

70.1.14.1.4. Options

Run mode:

worker

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

Configuration (in the stage’s own [stage-<name>] section):

PROGRAM

Must be pepsi-stage-relay-to-lmtp.

NEXT_STAGE

Required. Where a recipient the MDA could not deliver is routed (a bad destination mailbox, and the fallback for the reasons below). Set it to a pepsi-stage-bounce(1) to bounce undeliverable recipients, or to a relay stage (e.g. pepsi-stage-srs(1) → a smarthost) to forward them off-site. pepsi-setup rejects a configuration without it.

QUOTA_LIMIT_STAGE

Where to route a recipient the MDA refused for being over quota (RFC 3463 x.2.2). Defaults to NEXT_STAGE. Set it to a bounce stage when NEXT_STAGE is a relay, so quota rejects bounce rather than relay off-site.

SIEVE_REJECT_STAGE

Where to route a recipient refused by Sieve / local policy (RFC 3463 x.7.x). Defaults to NEXT_STAGE.

NOTIFY_STAGE

Where positive SUCCESS delivery notifications and DELAY warnings are routed (as a cloned message carrying state.bounce.kind); set it to a pepsi-stage-bounce(1), which renders the report. When unset, no positive or delay notifications are originated. The stage never builds a notification itself.

SOCKET

Path of the MDA’s LMTP UNIX-domain socket (e.g. /run/dovecot/lmtp). The common local-delivery layout; no TLS or authentication. Mutually exclusive with HOST.

HOST / PORT

Reach the MDA over TCP instead (PORT defaults to 24). Use for an MDA on another host.

TLS

Transport security for a HOST target: off (default), starttls, or tls (implicit). TLS_VERIFY (default yes) and TLS_CA control PKIX verification; TLS_CLIENT_CERT/TLS_CLIENT_KEY present a client certificate for mutual TLS / SASL EXTERNAL.

AUTH

SASL mechanism for a TCP target: none (default), plain, login or external. Authentication is refused over cleartext, so any non-none AUTH requires TLS = starttls or tls (and external requires a client certificate). USERNAME/PASSWORD supply the credential.

SERVER_NAME

Name announced in LHLO and stamped in the Received: header. Defaults to [pepsi-ingress] HOSTNAME.

MAX_LIFETIME, RETRY_INITIAL, RETRY_MAX_INTERVAL, RETRY_FACTOR

Retry/give-up timing for transiently-deferred recipients.

DELAY_DSN_AFTER

Emit a one-shot “delayed” DSN once a still-undelivered message has been queued this long and the sender asked for NOTIFY=DELAY.

CONNECT_TIMEOUT, COMMAND_TIMEOUT, DATA_TIMEOUT

Per-operation LMTP timeouts.

70.1.14.1.5. See Also

pepsi-dispatch(1), pepsi-stage-relay-to-maildir(1), pepsi-stage-aliases(1), pepsi-stage-bounce(1), pepsi.conf(5), pepsi.state(7).

RFC 2033 (LMTP), RFC 5228 (Sieve).