71. pepsi-stage-list

Decide which envelope recipients name a mailing list, and route them by role.

71.1. Role

pepsi-stage-list is the mailing-list router. For each envelope recipient it asks whether the address names a list on this server and, if so, which of a list’s nine addresses it is; the message is then routed to the stage that handles that role. It is the only one of the five list stages that runs on every message, so it is deliberately cheap: it loads the envelope and state and never the headers or the body, and a server that hosts no lists sees every message advance to NEXT_STAGE untouched. Reference: pepsi-stage-list(1).

This subsystem is a reimplementation of GNU Mailman 3; see Mailing lists, which names what was taken from upstream.

71.2. Features

  • Nine addresses per list. The posting address <list>@<host> plus the eight role sub-addresses -request, -join, -subscribe, -leave, -unsubscribe, -confirm+<token>, -owner and -bounces (which may carry VERP detail, -bounces+alice=example.net).

  • Posting address first, then suffixes longest-first. Both orders are load-bearing. A list may legitimately be called foo-request, and matching suffixes first would make it unreachable the moment somebody created a list called foo — with mail to a real list silently entering the command path as the symptom. Longest-first is why -unsubscribe is not read as -subscribe on a stem ending in -un.

  • Three destinations by role. The posting address goes to POST_STAGE, -bounces to BOUNCE_STAGE, and every other role — -owner included — to COMMAND_STAGE, which is the stage that already knows how to find a list’s owners and how to suppress an auto-reply loop.

  • Mixed recipients split. A message addressed to a list and to an ordinary mailbox, or to two lists, becomes one row per destination, each with its own recipients and its own slice of state.dsn.rcpt. The not-a-list group stays on the original row.

  • No staleness window. The routing table is loaded once per worker and refreshed by the dispatcher retiring its workers when the database announces a change on the pepsi_list_changed channel, so a list created while the dispatcher runs starts receiving mail with no restart. The stage holds no listener of its own: a worker’s pool is one connection, and a listener would hold it forever.

71.3. Two warnings

Warning

BOUNCE_STAGE on this stage means the opposite of what it means everywhere else in Configuration. Here it is where somebody else’s inbound bounce is consumed; everywhere else it is where Pepsi generates a DSN. Pointing it at pepsi-stage-bounce answers a bounce with a bounce — a mail loop, not a misrouted message — and pepsi-setup refuses that configuration.

Warning

Setting [pepsi] RECIPIENT_DELIMITER to - breaks list sub-addressing entirely: every sub-address is <list>-<role>, so the base of announce-owner@ becomes announce and mail meant for the owners is posted to the list instead. The same option is the delimiter the fan-out writes into each VERP envelope sender and the -confirm+<token> address, so the router always splits what the lists wrote.

71.4. Fusion

A fusion successor runs in-process only if its Load is satisfied by what the predecessor loaded. This stage loads metadata only, so it fuses its own advance to NEXT_STAGE — the not-a-list path, the one that runs on every message — but it cannot fuse into pepsi-stage-list-post, which needs the body. Routing an actual post therefore costs one ordinary stage transition.

71.5. Configuration

[stage-<name>]: PROGRAM = pepsi-stage-list, and NEXT_STAGE, POST_STAGE, COMMAND_STAGE and BOUNCE_STAGE, all required. Per-list configuration lives in the database rather than in the file; see pepsi-list and Mailing lists.

71.6. State

  • Inputs: the envelope rcpt_to.

  • Outputs: a state.list descriptor on each routed row — the list id, the role, the address the message arrived at, and whatever the sub-address carried (a -confirm token, a -bounces VERP address). Deliberately small: everything else a worker stage needs it reads from the database, which it is about to do anyway.

  • Transitions: advance (to NEXT_STAGE or to a role’s stage), and a fan-out when the recipients split.

71.7. See also

Mailing lists, pepsi-stage-list-post, pepsi-stage-list-command, pepsi-stage-list-bounce, pepsi-list, pepsi-stage-list(1).