28. pepsi-ingress

The inbound SMTP server (MTA) and the entry point of the pipeline.

28.1. Role

pepsi-ingress runs the SMTP server that receives mail, authenticates it at the boundary, and stores each accepted message as one pepsi.workqueue 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 pepsi-ingress(1) / pepsi.conf(5).

28.2. Features

28.2.1. 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 <Postmaster> (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.

28.2.2. 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/ESMTPA/ ESMTPSA (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).

28.2.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 pepsi-stage-arc stage, so ingress holds no ARC keys. Ingress records the session context the ARC stage needs under state.origin.

28.2.4. Submission (RFC 6409)

  • A listener with SUBMISSION = yes makes client authentication mandatory and applies the submission fixups (a missing Date:/Message-ID: is added). Any of four mechanisms authenticates a session: a peer address in MYNETWORKS, UNIX-socket peer credentials (AUTH_PEERCRED, the default on a UNIX listener and rejected on TCP), a SASL AUTH exchange (SASL_TYPE = dovecot, offered only over TLS) or a pinned TLS client certificate (TLS_AUTH_CLIENT). An authenticated session is recorded with state.local_origin = true; USERNAME_MAP decides which addresses the authenticated identity may send as.

  • Submission requires a TLS-protected MODE, except on a local UNIX-domain socket, where the kernel supplies the identity and there is no transport to protect.

  • The local submission socket used by pepsi-sendmail needs no listener section at all: ingress synthesises one for [pepsi-sendmail] SOCKET unless an explicit section already serves that path. SOCKET = none is the only switch that turns it off; failing to bind it is fatal.

28.2.5. Mailbox quota

  • When a mailbox quota policy is in force, a recipient whose measured usage is over its limit is refused at RCPT (452 4.2.2 or 552 5.2.2, per [pepsi] MAILBOX_OVER_QUOTA). Ingress only ever reads pepsi.mailbox_quota, and refuses only on a measurement younger than MAILBOX_QUOTA_MAX_AGE; every uncertainty (no row, a stale or absent measurement, a database error) accepts. MAILBOX_QUOTA_RCPT_CHECK = no (default yes) moves all enforcement to delivery time. See pepsi-quota.

28.2.6. DSN intake (RFC 3461)

  • Validates and stores RET/ENVID (MAIL FROM) and NOTIFY/ORCPT (RCPT TO) under state.dsn; malformed values are rejected 501.

28.2.7. 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.

28.2.8. 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 pepsi-stage-srs.

28.2.9. 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.

  • Wakes the dispatcher out of band and coalesced, at most once per DISPATCH_WAKE_INTERVAL (default 5 ms), after the row has committed. The admission transaction itself carries no NOTIFY: PostgreSQL holds a database-wide lock from the moment a transaction queues a notification until it commits, so a notifying insert cannot group-commit. A lost wake-up costs only the dispatcher’s POLL_INTERVAL of latency.

28.3. Configuration

Read from [pepsi-ingress] (acceptance policy, limits, timeouts, DNS), the [pepsi-ingress-listener-*] sockets (transport and client authentication, per listener), and the shared [pepsi-postgres], [pepsi], [pepsi-sendmail] and [pepsi-srs] sections. The database configuration overlay is applied on top. Full reference: pepsi.conf(5) and Configuration.

28.4. Outputs to the pipeline

Seeds the row’s state with origin (SMTP provenance), auth (the SPF/DKIM/DMARC verdicts), local_origin, spam = false for a message whose sole recipient is <Postmaster>, and, when requested, dsn (RFC 3461 parameters). See Architecture for the state model.

28.5. See also

pepsi-dispatch, pepsi-stage-arc, pepsi-stage-srs, Supported Features, pepsi-ingress(1), pepsi.conf(5).