50. pepsi-stage-route

Send each recipient to the next hop their domain selects.

50.1. Role

pepsi-stage-route makes the one routing decision a gateway in front of another mail system has on every inbound message: does this recipient belong to the system behind me, or to the internet? It picks a next stage per envelope recipient from that recipient’s domain, splitting the message when recipients disagree. It only moves the stage column — it never rewrites, delivers, bounces or drops a message, and state (including state.dsn) is preserved. Reference: pepsi-stage-route(1).

Getting that decision 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. See Microsoft Exchange as a gateway for the deployment this stage exists for.

50.2. Features

  • Managed domains by default. MANAGED_DOMAINS defaults to [pepsi-ingress] ACCEPTED_DOMAINS, so the set of domains treated as “behind the gateway” cannot drift from the set ingress actually accepts. With MANAGED_STAGE set, the whole gateway topology is two options.

  • Explicit rules for carve-outs. ROUTES takes whitespace- or comma-separated <domain>=<stage> pairs, consulted before the managed set, so one subdomain can go elsewhere without removing its parent from MANAGED_DOMAINS (which would also stop ingress accepting it).

  • Glob patterns, not regexes. * matches any run of characters and the match is anchored at both ends: *.example.org matches mail.example.org but not example.org nor mail.example.org.evil.test. A pattern that looks like a regular expression is rejected at configuration time rather than silently matching nothing.

  • Per-group recipient fan-out. Divergent recipients become sibling pending rows in one database call, each carrying only its own recipients with state.dsn.rcpt sliced to match rcpt_to.

  • Deterministic grouping. Groups are formed in first-seen order, so a retried message keeps the same recipients on the same row.

  • Metadata only. Neither the headers nor the body is loaded.

Note

Unlike the other metadata-only stages, this one does not default to FUSION = yes. It is a candidate on paper — it is folded into the unified binary and loads no message content — but a fused stage runs inside its predecessor’s worker, and this stage’s recipient fan-out refuses to run with a predecessor’s uncommitted content changes pending. Rather than make that interaction a run-time surprise, the stage is simply dispatched normally.

50.3. Loop prevention

pepsi-setup refuses — not warns about — two configurations:

  • one in which a message for a served domain could reach pepsi-stage-relay-to-internet, found by walking forward from every stage a managed recipient can reach. BOUNCE_STAGE is not followed: a bounce is a new null-sender message about a delivery that already failed, and it legitimately reaches a relay.

  • a [pepsi-stage-relay-to-smarthost-mta-*] section whose HOST is this host and whose PORT is one an ingress listener is bound to. Both halves are required — a site may legitimately run an unrelated relay on the same host, or reach a different machine on port 25.

They are refusals because 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.

50.4. Configuration

Option

Meaning

ROUTES

Whitespace/comma separated <domain-pattern>=<stage> pairs, consulted first, in order. Optional.

MANAGED_DOMAINS

The domains behind this gateway. Defaults to [pepsi-ingress] ACCEPTED_DOMAINS; at least one is required.

MANAGED_STAGE

Where a recipient at a managed domain goes. Optional; without it managed recipients take NEXT_STAGE, which pepsi-setup then has to prove is not a loop.

NEXT_STAGE

Where every other recipient goes. Required — the stage never delivers, bounces or drops, so every recipient must have somewhere to be.

50.5. Placement

On the inbound path, after authentication and decryption and before the relay stages — typically arcdecryptaliasesroute, with MANAGED_STAGE naming the smarthost stage that relays to Exchange and NEXT_STAGE naming the direct-to-MX stage.

50.6. See also