85.1.35. pepsi-stage-route¶
route each recipient to a next hop by their domain
- Manual section:
1
85.1.35.1.1. Name¶
pepsi-stage-route - the recipient-domain routing stage of the Pepsi pipeline.
85.1.35.1.2. Synopsis¶
pepsi-stage-route [GLOBAL-OPTIONS] worker
85.1.35.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.workqueue 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).
85.1.35.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. The
resulting set must not be empty: pepsi-setup(1) refuses a route stage with
no managed domains, since it could neither tell the two sides apart nor check
for the loop described below.
85.1.35.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.
85.1.35.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
every option that sends the message itself on: NEXT_STAGE, any further
routing stages’ targets, pepsi-stage-if(1)’s TRUE_STAGE and
FALSE_STAGE, pepsi-stage-milter(1)’s ACCEPT_STAGE and
QUARANTINE_STAGE, pepsi-stage-decrypt(1)’s QUARANTINE_STAGE,
pepsi-stage-encrypt(1)’s SECURE_LINK_STAGE,
pepsi-stage-secretary(1)’s UNCHALLENGEABLE_STAGE and
pepsi-stage-anti-spam(1)’s BOUNCE_TARGET_STAGE.
Three kinds of target are deliberately not followed. BOUNCE_STAGE and
the options that route a failed recipient towards a report
(pepsi-stage-milter(1)’s REJECT_STAGE, the local-delivery stages’
QUOTA_LIMIT_STAGE, SIEVE_REJECT_STAGE, NOTIFY_STAGE and
UNKNOWN_MAILBOX_STAGE): what travels there becomes a null-sender DSN about a
delivery that already failed, not the message continuing down the pipeline, and
a DSN legitimately reaches a relay. RESPONSE_STAGE and its kin, which inject
a new message to somebody else. And the targets that re-address the message to
new recipients the pipeline routes afresh — pepsi-stage-dot-forward(1)’s
RESTART_STAGE and the mailing-list stages’ POST_STAGE,
COMMAND_STAGE and DELIVERY_STAGE.
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.
85.1.35.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, plus RECIPIENT_CHECK
(none, probe or map) with PROBE_HOST, PROBE_PORT,
PROBE_TLS, PROBE_TIMEOUT and RECIPIENT_MAP. The last group is not
read by the stage: it tells pepsi-ingress(1) how to ask the backend
whether an address exists, so that an unknown one is refused at RCPT time
rather than bounced by the backend later. All are documented in
pepsi.conf(5).
85.1.35.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.
85.1.35.1.9. Commands¶
- worker
Run as a persistent pepsi-dispatch(1) worker, reading message ids on standard input.
85.1.35.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.
85.1.35.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.- 78
The worker refused to start: its section does not parse. The dispatcher requeues what it handed over and retries the stage later. A database error while a message is being split is retried like any other fault of this host (see pepsi-dispatch(1)); a retry finds the row already reduced to the recipients that stayed and routes them on their own.
85.1.35.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
85.1.35.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)
85.1.35.1.14. Bugs¶
Report bugs to the Pepsi issue tracker.