85.1.19. pepsi-stage-list¶
route mail addressed to a mailing list
- Manual section:
1
85.1.19.1.1. Name¶
pepsi-stage-list - the mailing-list router of the Pepsi pipeline.
85.1.19.1.2. Synopsis¶
pepsi-stage-list [GLOBAL-OPTIONS] worker
85.1.19.1.3. Description¶
pepsi-stage-list is a stage program run by pepsi-dispatch(1). It
looks at each envelope recipient, decides whether it names a mailing list on
this server, and routes the message accordingly. It is the only one of the
five list stages that runs on every message, so it is cheap by design: it
loads the envelope and state and never the headers or the body.
Pepsi’s mailing-list subsystem is a reimplementation of GNU Mailman 3: the
address vocabulary this stage recognises – the posting address and the eight
role sub-addresses – is upstream’s, copyright the Free Software Foundation and
its contributors, so that a site migrating from Mailman keeps every address its
subscribers already use. Files in Pepsi that came from GNU Mailman remain under
the GNU General Public License; see pepsi-list(1) for the fuller
statement and vendor/PEPSI-VENDORING.md for the list.
A server that hosts no lists is unaffected by it. With no lists configured the routing table is empty and every message advances to NEXT_STAGE, which is what makes dropping the section into an existing pipeline harmless.
85.1.19.1.4. Addresses recognised¶
For each recipient the stage matches, in this order:
the posting address
<list>@<host>;then the eight role sub-addresses
-request,-join,-subscribe,-leave,-unsubscribe,-confirm+<token>,-ownerand-bounces(optionally carrying VERP detail,-bounces+alice=example.net).
Posting addresses are matched before the suffixes, and the suffixes are
tried longest-first. Both orders matter. A list may legitimately be called
foo-request; matching suffixes first would make it unreachable the moment
somebody created a list called foo, and the symptom would be mail to a real
list silently entering the command path. Longest-first is why -unsubscribe
is not read as -subscribe with a stem ending in -un.
Warning
Setting [pepsi] RECIPIENT_DELIMITER to - breaks list
sub-addressing entirely. Every sub-address is <list>-<role>, so with
- as the delimiter the base of announce-owner@ is announce — the
posting address — 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.
85.1.19.1.5. Routing¶
A recipient that names a list is routed to one of three stages by role:
Role |
Stage option |
|---|---|
the posting address |
POST_STAGE |
|
BOUNCE_STAGE |
every other role |
COMMAND_STAGE |
-owner goes to the command stage even though it carries no command: it is
the stage that knows how to look up a list’s owners and how to suppress an
auto-reply loop, and giving it a fourth stage of its own would duplicate both.
A message addressed to a list and to an ordinary mailbox — or to two lists —
is split, one row per destination, each carrying its own recipients and its own
slice of state.dsn.rcpt. The not-a-list group, when there is one, stays on
the original row.
Warning
BOUNCE_STAGE here means the opposite of what it means on every other stage in pepsi.conf(5). On this stage 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(1) answers a bounce with a bounce — a mail loop rather than a misrouted message — and pepsi-setup refuses that configuration.
85.1.19.1.6. Freshness¶
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. A list created while the dispatcher is running
therefore starts receiving mail with no restart and no staleness window. The
stage itself holds no listener: a stage worker’s connection pool is exactly one
connection, and a listener inside the worker would hold it forever.
85.1.19.1.7. Fusion¶
A fusion successor may run in-process only if its Load is satisfied by what
the predecessor already loaded. This stage loads metadata only, so it can fuse
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(1), which
needs the body. Routing an actual list post costs one ordinary stage
transition.
85.1.19.1.8. Configuration¶
NEXT_STAGE, POST_STAGE, COMMAND_STAGE and BOUNCE_STAGE, all
four required, in the stage’s own [stage-<name>] section. Per-list
configuration is in the database, not here; see pepsi-list(1).
85.1.19.1.9. State¶
Inputs: the envelope rcpt_to.
Outputs: a state.list descriptor on each routed row — the list’s id,
the role, the address the message arrived at, and whatever detail the
sub-address carried (a -confirm token, a -bounces VERP address).
Deliberately small: everything else the worker stage needs it reads from the
database, which it is about to do anyway.
Transitions: advances (to NEXT_STAGE or to a role’s stage), and fans out when the recipients split.
85.1.19.1.10. Commands¶
- worker
Run as a persistent pepsi-dispatch(1) worker, reading message ids on standard input.
85.1.19.1.11. Global Options¶
The usual set — -c/–config, -L/–log, -v/–verbose, -h/–help and -V/–version — behaving as they do for every Pepsi program; see pepsi-config(1).
85.1.19.1.12. Exit Status¶
- 0
The message was routed.
- 1
An error occurred (a misconfigured stage, or a database error).
85.1.19.1.13. Examples¶
[stage-list]
PROGRAM = pepsi-stage-list
NEXT_STAGE = aliases
POST_STAGE = list-post
COMMAND_STAGE = list-command
BOUNCE_STAGE = list-bounce
85.1.19.1.14. See Also¶
pepsi-list(1), pepsi-stage-list-post(1), pepsi-stage-list-deliver(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)
85.1.19.1.15. Bugs¶
Report bugs to the Pepsi issue tracker.