42. pepsi-stage-relay-to-lmtp

Deliver locally over LMTP to an MDA (Dovecot) that runs Sieve.

42.1. Role

pepsi-stage-relay-to-lmtp is a local-delivery stage that hands each message to a Mail Delivery Agent — typically Dovecot — over LMTP (RFC 2033). The MDA files the message and, in doing so, runs the recipient’s Sieve (RFC 5228) script, so server-side filtering is delegated to the MDA instead of being implemented in Pepsi. It is an alternative to pepsi-stage-relay-to-maildir: LMTP gains Sieve and a clean per-recipient delivery status, and needs no setuid helper (the MDA performs the per-user delivery), at the cost of running a local IMAP/LMTP server.

42.2. LMTP-native delivery, deliver-and-route

All envelope recipients are offered in one LMTP transaction; the MDA returns one reply per recipient after end-of-DATA (RFC 2033 §4.2). The stage never builds a delivery-status notification itself — it delivers, and otherwise routes each recipient onward by reason, recording why under state.bounce for a downstream pepsi-stage-bounce (or a relay) to act on:

  • 2xx — delivered; a NOTIFY=SUCCESS notification (and DELAY warnings) is routed to the optional NOTIFY_STAGE;

  • a permanent reject is routed by its RFC 3463 enhanced status — a bad mailbox (x.1.x, x.2.x other than x.2.2, and a bare 550 with no enhanced status at all) to NEXT_STAGE, over-quota (x.2.2) to QUOTA_LIMIT_STAGE (else NEXT_STAGE), a Sieve / policy reject (x.7.x) to SIEVE_REJECT_STAGE (else NEXT_STAGE), anything else to NEXT_STAGE;

  • 4xx — transient: retry that recipient (exponential backoff) until MAX_LIFETIME, then route by the same classification.

Delivered, routed and still-deferred recipients are reconciled in one fan-out: the delivered recipients leave the row, each routed recipient becomes a sibling pending row sliced to itself (so a relay target never re-delivers to the others) at its target stage, and the row is reduced to the deferred recipients for the next retry. A failure of the whole session (connect, STARTTLS, authentication, data transfer) retries the whole message, or — when permanent or past MAX_LIFETIME — routes every recipient to NEXT_STAGE.

Because the MDA is the authority on which addresses are local, the stage does no locality check of its own — place it after alias expansion so only intended-local recipients reach it. NEXT_STAGE is mandatory (set it to a pepsi-stage-bounce to bounce, or to a relay stage to forward off-site); a message reaching the stage without one is marked failed before the MDA is contacted, so nothing is delivered.

42.3. Transport

The target is either a UNIX-domain socket (SOCKET, the common Dovecot layout: no TLS, no credentials) or TCP (HOST/PORT, PORT default 24) with optional TLS = off|starttls|tls and AUTH = none|plain|login|external. Authentication is refused over cleartext, so any AUTH other than none requires TLS. The shared LMTP client reuses the SMTP client’s transport, TLS and SASL machinery (pepsi_common::smtp::deliver_lmtp).

42.4. Configuration

[stage-<name>]: PROGRAM = pepsi-stage-relay-to-lmtp, NEXT_STAGE (mandatory), the optional reason targets QUOTA_LIMIT_STAGE / SIEVE_REJECT_STAGE / NOTIFY_STAGE, the target (SOCKET, or HOST/PORT with TLS, TLS_VERIFY, TLS_CA, TLS_CLIENT_CERT, TLS_CLIENT_KEY, AUTH and its USERNAME/PASSWORD), SERVER_NAME (the LHLO name; defaults to [pepsi-ingress] HOSTNAME), the timeouts (CONNECT_TIMEOUT/COMMAND_TIMEOUT/DATA_TIMEOUT) and the retry policy (RETRY_INITIAL/RETRY_MAX_INTERVAL/RETRY_FACTOR/MAX_LIFETIME/ DELAY_DSN_AFTER). pepsi-setup requires NEXT_STAGE and checks that each of the three reason targets names a real stage. This stage has no BOUNCE_STAGE: it never builds a DSN itself.

42.5. State

  • Inputs: state.dsn (per-recipient NOTIFY/ORCPT) and the retry bookkeeping it wrote on an earlier pass.

  • Outputs: state.bounce on each routed sibling row (recording why the MDA refused, or that the delivery succeeded for a SUCCESS/DELAY notification), and attempts/last_error (plus delay_sent once a delay warning was routed) on the deferred row. state.dsn.rcpt is sliced in lockstep with every row’s recipients.

42.6. See also

pepsi-stage-relay-to-maildir, pepsi-stage-aliases, pepsi-stage-bounce, pepsi-quota, Supported Features, pepsi-stage-relay-to-lmtp(1), pepsi.conf(5).