70.1.27. pepsi-stage-route¶
route each recipient to a next hop by their domain
- Manual section:
1
70.1.27.1.1. Name¶
pepsi-stage-route - the recipient-domain routing stage of the Pepsi pipeline.
70.1.27.1.2. Synopsis¶
pepsi-stage-route [GLOBAL-OPTIONS] worker
70.1.27.1.3. Description¶
pepsi-stage-route is a stage program run by pepsi-dispatch(1) as a
persistent worker reading message ids on standard input. It loads that
pepsi.ingress row (refusing to act unless its status is running),
reads its [stage-<stage>] section, and chooses a next stage for each
envelope recipient from that recipient’s domain.
It exists for one deployment shape: a gateway sitting in front of another mail system, such as Microsoft Exchange or Exchange Online. Such a gateway has one routing decision to make on every inbound message — does this recipient belong to the system behind me, or to the internet? — and getting it wrong is not a delivery failure but a mail loop. The domains behind the gateway are exactly the domains whose public MX is the gateway, so handing one to a direct-to-MX relay posts the message straight back into our own ingress, which accepts it, routes it out again, and repeats until MAX_HOP_COUNT stops it.
The stage never rewrites the message, never delivers, never bounces and never
drops one. The only column it moves is stage, so state — including
state.dsn — is preserved exactly. Neither the headers nor the body is
loaded.
Unlike the other metadata-only stages it does not default to
FUSION = yes. A fused stage runs inside its predecessor’s worker process, and
this stage’s recipient fan-out refuses to run while a predecessor’s content
changes are still uncommitted; rather than make that interaction a run-time
surprise, the stage is always dispatched normally. See pepsi-dispatch(1).
70.1.27.1.4. Routing¶
For each recipient, in this order:
the explicit ROUTES rules, in configured order — the first whose domain pattern matches wins;
the managed-domain set (MANAGED_DOMAINS, defaulting to
[pepsi-ingress] ACCEPTED_DOMAINS) — a recipient at one of those domains goes to MANAGED_STAGE, when it is set;otherwise NEXT_STAGE.
Explicit rules are consulted first so a carve-out is expressible: one subdomain of a served domain can be sent somewhere other than the system behind the gateway without removing the parent from MANAGED_DOMAINS, which would also stop ingress accepting mail for it.
Domain patterns are compared case-insensitively and may contain *, which
matches any run of characters. They are globs, not regular expressions, and
are anchored at both ends: *.example.org matches mail.example.org but
neither example.org itself nor mail.example.org.evil.test. A pattern
that looks like a regular expression is rejected at configuration time rather
than silently matching nothing.
MANAGED_DOMAINS defaults to what ingress accepts rather than making the
operator restate it: a routing table that has drifted from ACCEPTED_DOMAINS
is precisely a domain whose mail goes to the wrong side of the gateway.
70.1.27.1.5. Recipient fan-out¶
Recipients that route to the same stage travel together. When they diverge,
the message is split: this row keeps the first group and every other group
becomes a sibling pending row at its own target, all in one database call.
Each sibling carries only its own recipients, and each row’s
state.dsn.rcpt is sliced to match its rcpt_to, so a later bounce or
delivery report still describes the right addresses.
Grouping is by first-seen order, so a message that is retried keeps the same recipients on the same row.
70.1.27.1.6. Loop prevention¶
pepsi-setup(1) refuses a configuration in which a message for a served
domain could reach a direct-to-MX relay (pepsi-stage-relay-to-internet(1)).
It walks forward from every stage a managed recipient can reach, following
NEXT_STAGE and any further routing stages’ targets. BOUNCE_STAGE is
deliberately not followed: a bounce is a new null-sender message about a
delivery that already failed, not the message continuing down the pipeline, and
a bounce legitimately reaches a relay.
Separately, pepsi-setup(1) refuses a [pepsi-stage-relay-to-smarthost-mta-*]
section whose HOST is this host (its [pepsi-ingress] HOSTNAME,
localhost, or a loopback address) and whose PORT is one an ingress
listener is bound to. Both halves are required, because either alone is a
legitimate configuration.
These are refusals rather than warnings. A loop only stops when the relay
stages’ MAX_HOP_COUNT guard fires, tens of copies later, and the bounce that
guard then produces is itself addressed back into the loop.
70.1.27.1.7. Configuration¶
Options live in the stage’s own [stage-<name>] section
(PROGRAM = pepsi-stage-route): ROUTES, MANAGED_DOMAINS,
MANAGED_STAGE and the required NEXT_STAGE. They are documented in
pepsi.conf(5).
70.1.27.1.8. State¶
Inputs: the envelope recipients (rcpt_to). No member of state is
read.
Outputs: none — state is never modified, only sliced per recipient group
when a message is split. The state layout is described in pepsi.state(7).
Transitions: advances to the stage each recipient’s domain selects, and
splits the message into sibling pending rows when recipients disagree. The
stage never pauses, fails, reroutes or finishes.
70.1.27.1.9. Commands¶
- worker
Run as a persistent pepsi-dispatch(1) worker, reading message ids on standard input.
70.1.27.1.10. Global Options¶
- -c FILE, –config FILE
Read the configuration from FILE instead of searching the default locations.
- -L LOGLEVEL, –log LOGLEVEL
Set the logging verbosity (default
info).- -v, –verbose
Show log messages from all sources.
- -h, –help; -V, –version
Print a usage summary / the version and exit.
70.1.27.1.11. Exit Status¶
- 0
The message was routed (advanced, and split first if its recipients diverged).
- 1
An error occurred (message not found or not
running, misconfigured stage — e.g. a missing NEXT_STAGE — or a database error). The reason is written to the log.
70.1.27.1.12. Examples¶
Route a single message (it must be running):
echo 42 | pepsi-stage-route -c /etc/pepsi/pepsi.conf worker
The gateway topology in two options — mail for the domains this host serves goes to Exchange, everything else out to the internet:
[stage-route]
PROGRAM = pepsi-stage-route
MANAGED_STAGE = exchange
NEXT_STAGE = internet
The same, with one legacy subdomain carved out and a partner domain given its own next hop:
[stage-route]
PROGRAM = pepsi-stage-route
ROUTES = legacy.example.org=maildir partner.example=partner-relay
MANAGED_STAGE = exchange
NEXT_STAGE = internet
70.1.27.1.13. See Also¶
pepsi-config(1), pepsi-stage-if(1), pepsi-stage-relay-to-smarthost(1), pepsi-stage-relay-to-internet(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)
70.1.27.1.14. Bugs¶
Report bugs to the Pepsi issue tracker.