.. 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-ingress ============= *The inbound SMTP server (MTA) and the entry point of the pipeline.* Role ==== ``pepsi-ingress`` runs the SMTP server that receives mail, authenticates it at the boundary, and stores each accepted message as one ``pepsi.ingress`` row at ``stage = init`` before notifying the dispatcher. It is the only component that talks to external sending clients; everything downstream operates on the stored row. The exhaustive option reference is :manpage:`pepsi-ingress(1)` / :manpage:`pepsi.conf(5)`. Features ======== Reception (RFC 5321) -------------------- * SMTP/ESMTP server with per-listener transport: **cleartext**, **STARTTLS** upgrade (RFC 3207) and **implicit TLS** (RFC 8314); listeners bind TCP, UNIX or systemd-activated sockets. * Message bodies via **DATA** or **CHUNKING/BDAT** (RFC 3030). * Advertises **SIZE** (enforcing ``MAX_MESSAGE_SIZE`` → ``552``), **PIPELINING**, **ENHANCEDSTATUSCODES**, **8BITMIME** (RFC 6152), **SMTPUTF8** (RFC 6531) and **DSN** (RFC 3461). * Accepts mail only for ``ACCEPTED_DOMAINS`` (others → ``550`` relaying); always accepts the domainless ```` (RFC 5321 §4.5.1). * **Durable acceptance:** returns ``250`` only after the row commits; a database outage yields a transient ``451``. * Connection concurrency bounded by ``MAX_CONNECTIONS`` and a ``MAX_OPEN_SOCKETS`` file-descriptor budget; per-source rate limiting (``CONN_RATE_PER_SECOND``) and, under pressure, eviction of the longest-idle connection in the greediest source range guard against connection abuse. * Per-command read timeouts (``COMMAND_TIMEOUT``/``DATA_TIMEOUT``, RFC 5321 §4.5.3.2) close slow or stalled sessions, including a hung ``STARTTLS`` handshake. * **Lenient line lengths.** The RFC 5321 §4.5.3.1.4/§4.5.3.1.6 limits (512-octet command lines, 1000-octet text lines) bind *senders*; per the same section's guidance that receivers be liberal, ingress accepts longer lines. The internal ``MAX_CMD_LINE``/``MAX_DATA_LINE`` caps are memory-exhaustion guards only — a line with no terminator that grows past the cap drops the connection — not conformance enforcement. Trace header ------------ * Prepends an RFC 5321 §4.4 ``Received:`` header before authentication (so the ARC seal covers it), recording the client (HELO/EHLO, reverse-DNS, IP), this server, the transmission type ``SMTP``/``ESMTP``/``ESMTPS`` (RFC 3848) and the time; a ``for`` clause is added for single-recipient transactions. For an encrypted session the negotiated TLS version and cipher are appended as a ``(version=… cipher=…)`` comment after the ``ESMTPS`` keyword (RFC 8314 §4.3). Authentication (recorded at the boundary) ----------------------------------------- * **SPF** (RFC 7208), **DKIM** verification (RFC 6376 / RFC 8463) and **DMARC** (RFC 7489), plus **iprev/FCrDNS** (RFC 8601). * Prepends an **Authentication-Results** header (RFC 8601) with the ingress ``HOSTNAME`` as ``authserv-id``; the verdicts are also stored under ``state.auth`` (``spf``/``dkim``/``dmarc``/``arc``). * **Fail-open:** only a definite DMARC failure under ``DMARC_ENFORCE`` rejects (``550``); DNS/parse errors are recorded and the mail accepted. * ARC verify/seal is **not** done here — it is the :doc:`pepsi-stage-arc` stage, so ingress holds no ARC keys. Ingress records the session context the ARC stage needs under ``state.origin``. DSN intake (RFC 3461) --------------------- * Validates and stores ``RET``/``ENVID`` (``MAIL FROM``) and ``NOTIFY``/``ORCPT`` (``RCPT TO``) under ``state.dsn``; malformed values are rejected ``501``. Internationalised / 8-bit intake -------------------------------- * Accepts raw 8-bit bodies and UTF-8 headers/envelope; records the ``BODY=`` declaration under ``state.origin.body_8bit``. ``BINARYMIME`` is rejected (``501``). Per-hop re-advertising/downgrading happens later in the relay stages. SRS reverse decode ------------------ * A recipient in ``SRS_DOMAIN`` whose local-part is a valid SRS token is decoded to the original sender and accepted for relay there (even outside served domains — the HMAC authorises it); a forged/expired token is rejected (``550``). This is how bounces return to original senders. See :doc:`pepsi-stage-srs`. Storage and notification ------------------------ * Stores the message **split** into ``headers`` and ``body`` columns, with the envelope, parsed ``From:``/``Subject:`` and the verdicts; inserts at ``stage = init`` / ``status = pending`` and issues ``NOTIFY ingress``. Configuration ============= Read from ``[pepsi-ingress]`` (acceptance policy and DNS), the ``[pepsi-ingress-listener-*]`` sockets, the shared ``[pepsi-postgres]``, ``[pepsi]`` and ``[pepsi-srs]`` sections. Full reference: :manpage:`pepsi.conf(5)` and :doc:`../configuration`. Outputs to the pipeline ======================= Seeds the row's ``state`` with ``origin`` (SMTP provenance) and, when requested, ``dsn`` (RFC 3461 parameters). See :doc:`../architecture` for the state model. See also ======== :doc:`pepsi-dispatch`, :doc:`pepsi-stage-arc`, :doc:`pepsi-stage-srs`, :doc:`../features`, :manpage:`pepsi-ingress(1)`, :manpage:`pepsi.conf(5)`.