.. This file is part of PEPSI. Copyright (C) 2026 Pepsi contributors PEPSI is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. =================== pepsi-stage-aliases =================== *Expand envelope recipients through an alias / mailing-list map.* Role ==== ``pepsi-stage-aliases`` rewrites a message's envelope recipients through a mapping file before delivery: every recipient that matches an entry in the map is replaced by that entry's target addresses, recipients that match nothing are left unchanged, the resulting list is de-duplicated so each address is delivered to at most once, and the message advances to ``NEXT_STAGE``. It is the classic ``/etc/aliases`` / virtual-domain expansion step, and is normally placed just before local delivery. It loads only the envelope and ``state`` — never the headers or body — and never pauses, fails or finishes a message. Reference: :manpage:`pepsi-stage-aliases(1)`. Features ======== * **Postfix-style map file.** ``ALIASES`` names a human-readable file parsed like a Postfix :manpage:`virtual(5)` table: each non-blank line maps one *key* — a full address, an ``@domain`` catch-all, or a ``*``-glob such as ``sales-*@example.org`` — to one or more comma/whitespace-separated target addresses (kept verbatim; targets may not themselves contain a wildcard). A key with no targets is ignored, so a recipient is never silently deleted. * **Most-specific match wins.** A recipient is lower-cased and looked up verbatim; on a miss the ``@domain`` catch-all is tried, then the most specific matching ``*`` wildcard (a narrow ``sales-*@example.org`` beats a broad ``*@example.org`` beats a bare ``*``). Sub-address (``+tag``) detail is **not** stripped, so a specific list address can be aliased on its own. * **Transitive expansion with loop breaking.** When a target is itself a key it is expanded in turn, so nested distribution lists resolve to their leaf addresses; a cycle is broken by resolving the repeated key to its literal address rather than recursing forever. * **mtime-cached, fail-soft.** The map is read into a process-global cache and re-parsed only when the file's modification time changes, so a busy worker does not re-read it per message. A missing or unreadable file is treated as an empty map (and logged), so a momentary misconfiguration does not fail mail. ``pepsi-setup`` syntax-checks the file at install time when it exists. Delivery Status Notifications ============================= ``state.dsn.rcpt`` runs parallel to the recipient list, so it is rebuilt in lockstep with the rewritten recipients. An expanded target inherits the ``NOTIFY`` of the original recipient and, following RFC 3461 §5.2.7, carries an ``ORCPT`` pointing at that original recipient (its existing ``ORCPT`` if any, otherwise ``rfc822;``). An unexpanded recipient keeps its DSN entry, and message-level ``RET``/``ENVID`` and every other ``state`` key are preserved. ``pepsi-stage-aliases`` is the only stage that writes a brand-new ``rcpt_to`` (via the ``rewrite_recipients`` helper); the other recipient helpers only *reduce* the list. Configuration ============= ``[stage-]``: ``PROGRAM = pepsi-stage-aliases``, the ``ALIASES`` map-file path, and the ``NEXT_STAGE`` the expanded message advances to. ``pepsi-setup`` creates a starter ``ALIASES`` template when the file is missing and syntax-checks it when present. See :manpage:`pepsi-stage-aliases(1)` and :doc:`../configuration`. State ===== * **Inputs:** the envelope ``rcpt_to`` and the parallel ``state.dsn.rcpt``. * **Outputs:** a rewritten ``rcpt_to`` and matching ``state.dsn.rcpt`` (only when an alias applied); no other ``state`` is touched. * **Transitions:** advance to ``NEXT_STAGE`` (the only transition). See also ======== :doc:`pepsi-stage-relay-to-maildir`, :doc:`pepsi-stage-relay-to-lmtp`, :doc:`pepsi-stage-if`, :doc:`../features`, :manpage:`pepsi-stage-aliases(1)`.