1. Introduction

1.1. What Pepsi is

Pepsi is a mail transfer agent: it receives mail on its own SMTP server, authenticates it, and then either delivers it to a local mailbox or re-sends it to the real destinations. It is built as a series of small, single-purpose stage programs chained over one shared database table, so each processing step — ARC sealing, SRS rewriting, DKIM signing, bounce generation, delivery — is an independent program that can be reordered, replaced or extended.

Which of those two roles a deployment uses is a configuration question. Point the tail of the pipeline at a relay stage and Pepsi is a forwarder in front of another mail system; point it at a local-delivery stage and Pepsi is the final destination. Both are ordinary configurations, and a single deployment routinely does both, choosing per recipient.

It is also an end-to-end crypto gateway. The same pipeline that repairs authentication can sign and encrypt outgoing mail as its author, and decrypt and verify incoming mail for the recipients this host serves, in OpenPGP or S/MIME, doing on the server what would otherwise need a plugin in every user’s mail client. Everything that makes that practical is part of the system: a key store, automatic discovery and publication of keys, and a browser portal for the common case of a correspondent who has no key at all. See The encryption problem below.

Pepsi is written in Rust, organised as a Cargo workspace, and licensed under the GNU Affero General Public License v3.0 or later. It reuses configuration, CLI and database machinery from the vendored GNU Taler Rust libraries.

1.2. The forwarding problem

When a boundary MTA forwards mail, it sends from its IP addresses under the original sender’s envelope domain. This breaks two of the three big authentication mechanisms:

  • SPF fails, because the original domain’s SPF record does not list the forwarder’s IPs.

  • DKIM alignment is often broken, because list software and forwarders alter the message.

A naive forwarder therefore turns legitimate mail into mail the final receiver scores as forged. Pepsi addresses this on two fronts:

  • It records the verdict at the boundary — verifying SPF/DKIM/DMARC on arrival and sealing the result into an ARC chain (RFC 8617) — so the final receiver can trust what Pepsi saw even though forwarding broke the original signals.

  • It repairs the envelope with SRS (Sender Rewriting Scheme) so SPF passes at the next hop, and re-DKIM-signs outbound mail under a domain Pepsi controls.

1.3. The encryption problem

End-to-end mail encryption has existed for thirty years and is still barely used. The reason is not the cryptography, it is that every participant has to install, configure and maintain something — and then a correspondent who did not turns the whole exchange back into cleartext.

Pepsi moves the work to the server:

  • pepsi-stage-encrypt signs a locally submitted message with the From: author’s key and encrypts it to each recipient, in OpenPGP (PGP/MIME) or S/MIME (CMS). Every encrypted recipient gets their own ciphertext on their own queue row, so there is no recipient-list disclosure and Bcc leakage is structurally impossible.

  • pepsi-stage-decrypt is the inbound half: it opens whatever ciphertext arrived, verifies whatever signature it carries, records a deliberately precise verdict, and strips any security indicator the sender forged. The user reads ordinary mail in their usual client.

  • The keys come from a key store filled by an asynchronous discovery layer — WKD, DANE (OPENPGPKEY/SMIMEA), the VKS key-server protocol, LDAP and harvesting from inbound mail — each source ranked by how much its provenance is worth. Our own users’ keys are published the same ways. Key management is the chapter on all of this.

  • When a correspondent publishes no key, the choice is not only “cleartext or bounce”: the The secure-link fallback portal portal stores the message encrypted under a freshly generated PIN and mails the recipient a link. A stored message cannot be read from the database or a backup without the PIN — but the server handled the plaintext when the message was submitted, so this protects against a passive reader of stored data, not against the operator.

Because the gateway sits at the boundary rather than in the client, it works for users who install nothing, and it can be put in front of an existing mail system that will never grow these features itself — see Microsoft Exchange as a gateway. What it cannot be is end-to-end in the strict sense: the server sees plaintext, which is the trade being made, and Key management says so plainly.

1.4. The shape of the system

Every component shares one PostgreSQL database (the single pepsi schema) and one INI-style configuration file; they are only useful in concert.

  1. pepsi-ingress is the inbound SMTP server (MTA). It accepts a message, authenticates it, stores it as one row in the pepsi.workqueue table and notifies the pipeline.

  2. pepsi-dispatch is the single coordinator. It claims newly queued rows and feeds each to a persistent worker process of the row’s stage; a stage that is done with a message moves the row to the next stage itself, and the dispatcher claims it there on its next round.

  3. The pepsi-stage-* programs are the stages: ARC, SRS, DKIM signing, encryption and decryption, bounce generation, alias expansion, per-recipient routing, policy filters (whitelisting, pay-to-send, confirm-to-send, language, post-queue milters and per-address settings), the vacation responder, the mailing-list stages, two off-site relay stages, the local-delivery stages (Maildir, LMTP, ~/.forward) and a test sink. Each runs as a persistent worker (PROGRAM worker, reading message ids on standard input).

  4. pepsi-keydisc is the asynchronous key-discovery service — one instance per method — that the crypto stages hand a lookup to instead of performing network I/O of their own, and pepsi-keys is the operator CLI for the key store.

  5. pepsi-httpd is the HTTP/HTTPS server: it serves the MTA-STS policy file, the Web Key Directory endpoints that publish our users’ keys, the The secure-link fallback portal portal, and — on listeners explicitly flagged for it — a Prometheus /metrics page, the The administrative API and the browser The administration console.

  6. pepsi-setup provisions the schema, keys and DNS; pepsi-queue inspects and repairs the queue.

A message is a state machine over a single table: its stage column names the configuration section of the program currently responsible for it, and its status says whether that step is pending, running, paused, failed or timeout. The full model is described in Architecture.

1.5. Features

  • Authentication-preserving forwarding. SPF, DKIM and DMARC are verified on arrival and the verdict is sealed into an ARC chain (RFC 8617).

  • Envelope repair. SRS rewrites the envelope sender so SPF passes at the next hop, and outbound mail is re-DKIM-signed under a domain Pepsi controls.

  • End-to-end encryption and signing, done by the server. pepsi-stage-encrypt encrypts outgoing mail per recipient in OpenPGP (PGP/MIME, RFC 3156) or S/MIME (CMS, RFC 8551), signed with the From: author’s key; by default (ENABLE_PEP) every local sender at a served domain gets a key automatically. pepsi-stage-decrypt opens and verifies inbound ciphertext and records the verdict. No client plugin needed. See Key management.

  • Automatic key discovery and publication. A correspondent’s key is found through WKD, DANE (OPENPGPKEY/SMIMEA), the VKS key-server protocol, LDAP and harvesting from inbound mail, each ranked by how much its provenance is worth; our own users’ keys are published over WKD, DNS and — only when asked for — a key server.

  • A secure-link portal for correspondents with no key. The secure-link fallback portal stores the message encrypted under a freshly generated PIN and mails the recipient a link, instead of choosing between cleartext and a bounce.

  • Mailing lists. A reimplementation of GNU Mailman 3 — moderation, digests, bounce processing, an archive, and Mailman’s own REST API, so Postorius and HyperKitty work against it — built as pipeline stages. See Mailing lists.

  • An administrative API and a browser console. The administrative API exposes the queue, health, key store, logs and configuration under /api/v1, and the The administration console console is a client of that same documented surface — served only on listeners explicitly flagged for administration.

  • A pipeline of single-purpose stages. ARC, SRS, DKIM signing, alias expansion, whitelisting, language detection, a pay-to-send anti-spam gate, a confirm-to-send challenge, bounce generation, per-recipient routing, and several delivery back-ends — each an independent pepsi-stage-* program wired together by configuration.

  • Modern transport security. STARTTLS and implicit TLS, MTA-STS, DANE/TLSA (RFC 7672) and outbound TLS Reporting (RFC 8460).

  • Standards-complete SMTP. DATA and CHUNKING/BDAT, 8BITMIME and SMTPUTF8 with per-hop downgrade, full DSN (RFC 3461) support, and RFC 6409 message submission with SASL authentication.

  • Several delivery back-ends. Direct-to-MX delivery, relaying through an authenticated smarthost (many SASL mechanisms, OAuth and mutual TLS), or local delivery — into a user’s Maildir, through a per-user ~/.forward, or over LMTP to an MDA such as Dovecot (which then runs the recipient’s Sieve script).

  • A database-backed queue. One PostgreSQL schema is the queue, the state machine and the audit surface; operators inspect and repair it with ordinary SQL and the bundled CLI tools.

  • Extensible in any language. A stage is just a program that reads message ids on standard input; new stages need not be written in Rust.

Danger

Pepsi is highly experimental, immature and unsupported software. Its first release, 0.0.0, is not a stable one, and it has not been audited for production use. There is no warranty and no support. Do not place Pepsi on the path of mail you cannot afford to lose, and do not rely on it for anything important. Use it to learn, to experiment and to contribute — not to run critical infrastructure.

What is promised, from 0.0.0 on: a later release upgrades an earlier release’s database in place, without losing the queue or anything else it holds (see Upgrading). Configuration options, command-line options and the administrative API may still change between releases; each change is listed in NEWS.

1.6. Pepsi compared to other MTA designs

Pepsi is one point in a long-explored design space. Comparing it with the best-known free-software mail systems — Postfix, qmail, sendmail, Exim and the newer all-in-one Stalwart — shows the trade-offs Pepsi makes. Each design buys some properties at the cost of others.

1.6.1. Points in the design space

Postfix is a multi-process MTA. A master daemon supervises a fixed set of specialised, mostly long-lived daemons — smtpd to receive, cleanup to canonicalise, qmgr to schedule, and transport daemons such as smtp and local to deliver — which cooperate through on-disk queue directories and a small set of IPC protocols. It is a general-purpose MTA: local delivery, virtual domains, complex routing, content filtering and relaying are all first-class.

qmail takes modularity and compartmentalisation to an extreme: many tiny programs, several running under their own dedicated user accounts, each doing one small job and communicating through the filesystem and simple pipes. The design prizes security through least privilege and simplicity, and a Maildir queue that avoids file-locking hazards. Its core has been essentially frozen for a long time, so modern features (TLS, SMTP AUTH, DKIM) arrive through third-party patches and wrappers rather than the base distribution.

sendmail is the original Internet MTA and the archetype of the monolithic, endlessly configurable design: a single daemon does the work, driven by the famously arcane sendmail.cf (normally generated from m4 macros). Its longevity is unmatched and it introduced the milter plugin protocol that Postfix and others later adopted, but its configuration complexity and historical security record have pushed most new deployments toward other designs.

Exim is a single monolithic binary whose behaviour is governed by one exceptionally expressive runtime configuration language — string expansions and ACLs evaluated at each SMTP phase. Routing and policy decisions need no external helper programs, at the cost of a configuration that is easy to get subtly wrong. It is the default MTA on Debian and has a large, mature deployment base.

Stalwart is a modern all-in-one mail server written, like Pepsi, in Rust. Unlike every system above it is not only a transfer agent: it bundles IMAP4, JMAP and POP3 access, message storage and built-in spam filtering alongside a full SMTP server with native SPF/DKIM/DMARC/ARC, MTA-STS and DANE. It represents a different modern path from Pepsi’s — one integrated server that does everything rather than a chain of small programs.

Pepsi keeps the small-program philosophy but replaces the filesystem queue with a single PostgreSQL table that is simultaneously the queue, the per-message state machine and the operational audit log. A single dispatch coordinator drives pools of persistent stage worker programs, advancing each message from one stage to the next by updating its row. Authentication for the era of SPF/DKIM/DMARC is part of that core flow rather than bolted on. Where it differs most from the others is as an end-to-end crypto gateway: it signs, encrypts, decrypts and verifies OpenPGP and S/MIME on the server, manages and discovers the keys to do so, and offers a browser portal for correspondents who have no key at all.

1.6.2. At a glance

The table below summarises where these designs differ. The cells are terse — “built-in” versus “via add-ons” hides a great deal — but they show the shape of each system: both the capabilities Pepsi does not have and the two or three it has that the others do not.

Note

Only the Pepsi column has been checked against this source tree. The other five columns summarise those projects’ own documentation as of writing; they are offered for orientation, not as an audit, and a project that has added a feature since will be understated here.

Dimension

Postfix

qmail

sendmail

Exim

Stalwart

Pepsi

Language

C

C

C

C

Rust

Rust

Process model

Daemon set (master + helpers)

Many tiny programs

Monolithic daemon

Single binary, forked per task

Single async server

Dispatcher + stage-program chain

Primary role

General-purpose MTA

General-purpose MTA

General-purpose MTA

General-purpose MTA

All-in-one server (MTA + IMAP/JMAP + store)

MTA + crypto gateway; forwarding or local delivery per recipient

Configuration

main.cf/master.cf + maps

Control files + filesystem conventions

m4-generated sendmail.cf (arcane)

One runtime config: ACLs + string expansion

TOML + web admin

One INI file + database

Queue / state store

On-disk spool directories

Maildir-style queue

On-disk queue

On-disk spool + hints DB

Embedded / SQL store

PostgreSQL table (SQL-queryable)

Maturity

Decades; very widely deployed

Decades; core frozen, patch ecosystem

Oldest (1980s); proven but declining

Decades; Debian default

Young; maturing quickly

Experimental; first release 0.0.0

Performance

High, well-tuned

High, lightweight

Moderate

High

High (async Rust)

Modest; coordination/DB-bound

Ease of installation

Easy; packaged everywhere

Hard; build + patches

Hard; arcane config

Easy package, complex config

Easy; single binary + setup

Moderate; needs PostgreSQL + pepsi-setup

TLS / MTA-STS / DANE

TLS, DANE; MTA-STS via add-on

Via patches

TLS; STS/DANE via add-ons

TLS, DANE; MTA-STS via add-on

TLS, MTA-STS, DANE built-in

TLS, MTA-STS, DANE, TLSRPT built-in

Extension / filter API

Milter + policy delegation

Pipes / QMAILQUEUE

Milter (originator)

Routers/transports + local-scan + pipes

Sieve + webhooks

Insert a stage program (stdin/DB), any language; milter client (post-queue)

1.6.3. MTA features Pepsi does not have

Postfix, Exim and sendmail cover ground Pepsi does not:

  • Mailbox storage formats. Pepsi’s own local delivery writes only Maildir/new; there is no mbox writer. It does enforce mailbox quotas (see pepsi-quota), in the Maildir++ maildirsize format Dovecot, Courier and Exim also read, optionally backed by the kernel’s own quotactl(2) accounting — and an over-quota rejection from an MDA reached over LMTP is still routed rather than prevented, since there the mailbox is the MDA’s to account for.

  • Sieve, procmail and the rest of the user-filtering ecosystem. Pepsi implements no Sieve engine itself. It hands the message to a Mail Delivery Agent over LMTP — pepsi-stage-relay-to-lmtp, typically talking to Dovecot — which runs the recipient’s Sieve script as it files the mail. So a Pepsi deployment can offer Sieve; the filtering is the MDA’s, not Pepsi’s. There is no procmail equivalent, though per-user ~/.forward files are supported by pepsi-stage-dot-forward, including the classic |program and /file directives — each behind its own ALLOW_PIPE / ALLOW_FILE switch, run as the user by a privileged helper that drops to them first. The one user-filtering action Pepsi implements itself is the vacation auto-responder (pepsi-stage-vacation), because it has to be a pipeline decision: the reply is a new message that must be signed and relayed, and it must not be sent to a mailing list, a bounce or a spammer — which is knowledge the pipeline has and an MDA does not.

  • Arbitrary routing and address rewriting. There are no transport maps, canonical/relocated/rewrite tables or per-recipient relay tables, and no backup-MX/secondary-queue role for other domains. Routing is a choice between configured stages — pepsi-stage-route picks one per recipient from the recipient’s domain, which is enough to front an existing mail system — rather than an arbitrary topology, and rewriting is limited to SRS plus the alias map.

  • A pre-queue filter interface. Pepsi speaks the milter protocol — pepsi-stage-milter is a milter client, so opendkim, rspamd, clamav-milter and the rest can be used unchanged — but it runs them post-queue, after ingress has already accepted the message. A filter’s REJECT therefore produces a bounce where an MTA would have answered 5xx inside the SMTP session. There is no policy-delegation socket, and the native extension point remains inserting a stage program into the pipeline.

  • Integrated content scanning. Beyond the optional pay-to-send and confirm-to-send gates and the language/whitelist stages, there is no built-in virus or spam scanning, greylisting, DNSBL/RBL checking or reputation filtering — those are reached by pointing pepsi-stage-milter at the corresponding filter daemon.

1.6.4. Features that usually live outside the MTA

Several capabilities are not part of what an MTA has to do, and most mail systems reach them through something else: a milter, an add-on daemon, the mail delivery agent, or the user’s own client. This is where Pepsi differs most from the traditional designs. The caveat above applies to this table too.

Capability

Postfix

qmail

sendmail

Exim

Stalwart

Pepsi

SPF/DKIM/DMARC/ARC/SRS

Via milters / add-ons

Third-party patches

Via milters

DKIM built-in; rest via ACLs/add-ons

Built-in (all)

Built-in core (verify + ARC seal + SRS)

Spam protection

Milters, RBL/DNSBL, greylisting, policy

Add-ons (qmail-scanner, …)

Milters (SpamAssassin, …)

ACLs, RBL/DNSBL, greylisting, content scan

Built-in spam filter, DNSBL, greylisting

Pay-to-send gate, confirm-to-send, whitelist, language filter; milters (post-queue) for RBL/AV/greylisting

User filtering (Sieve)

The MDA (Dovecot)

The MDA (maildrop, procmail)

The MDA (procmail)

Own filter language, or the MDA

Built-in Sieve

The MDA over LMTP; ~/.forward and a vacation stage run in-pipeline

Mailbox access (IMAP/POP)

No (pair with Dovecot)

No (pair with a POP/IMAP server)

No (pair with Dovecot)

No (pair with Dovecot)

Yes (IMAP4 + JMAP + POP3)

No (pair with Dovecot; LMTP hand-off built in)

End-to-end mail encryption

No (client-side, or a separate gateway)

No

No

No

Partial (encrypt-at-rest under the user’s own key)

Server-side OpenPGP and S/MIME: sign + encrypt outbound, decrypt + verify inbound; secure-link portal for a keyless recipient

Correspondent key management

No (TLS and DKIM keys only)

No

No

No

Own users’ keys, for encrypt-at-rest

Key store + discovery (WKD, DANE, VKS, LDAP, harvest) + publication (WKD, OPENPGPKEY/SMIMEA, key server)

Administrative API / web console

No (files + postfix/postqueue)

No

No

No (files + exim utilities)

Yes (REST API + web admin)

Yes (/api/v1 + a browser console, admin-flagged listeners only)

Mailing lists are another such capability, normally a separate list manager (GNU Mailman, ezmlm, Sympa) wired to the MTA through alias maps. Pepsi’s is a reimplementation of GNU Mailman 3 that runs as pipeline stages, routes the list addresses inside its own pipeline with no alias file to regenerate, and serves Mailman’s REST API so Postorius and HyperKitty drive it unchanged; see Mailing lists.

Pairing with an MDA is direct. pepsi-stage-relay-to-lmtp hands messages over LMTP (RFC 2033), which is how Dovecot expects to receive them and is what lets it run each recipient’s Sieve script as it files the mail. pepsi-setup detects a local Dovecot during the interview and offers to write the socket configuration it needs, and pepsi-stage-relay-to-maildir writes Maildir directly for a site that would rather Dovecot simply read the spool.

1.6.5. Queue and state: files versus a database

  • A file-based queue (Postfix, qmail, sendmail, Exim) has no external dependency, is well understood, and scales with the filesystem; each message is an independent unit, which keeps failure modes local and simple. Introspecting or reporting across the whole queue, however, means walking files, and cross-message operations are not transactional.

  • A database-backed queue (Pepsi) makes the whole queue queryable with SQL, gives transactional multi-row state changes (splitting a message per recipient, fanning out bounces) and makes operator tooling and metrics straightforward. The cost is a hard dependency on PostgreSQL, a shared component whose availability and scaling characteristics now bound the whole system, rather than the per-message isolation a directory of files provides.

1.6.6. Process model: monolith, daemon set, or program chain

  • qmail and Pepsi favour many small programs: each is easy to audit in isolation, faults are contained, and components can run at different privilege levels. The price is more inter-process communication, more processes to schedule, and the coordination logic that ties them together (qmail’s filesystem conventions; Pepsi’s dispatcher and shared table).

  • Postfix occupies a middle ground: a fixed, carefully designed set of cooperating daemons rather than either a single monolith or an open-ended chain. This combines good performance with strong compartmentalisation, at the cost of a design that is fixed rather than freely re-composable.

  • Pepsi’s chain is re-composable by configuration: stages can be reordered and new ones inserted without recompiling, and a stage can be written in any language because the integration surface is just standard input and the shared table. The flip side is that the pipeline itself is the integration contract — the milter protocol is spoken as a client (post-queue only) rather than offered as a native plugin surface, and there is no policy-delegation interface.

1.6.7. Scope and maturity

  • Scope. Postfix, qmail, sendmail and Exim can run a site’s entire mail system, and Stalwart goes further still by bundling mailbox access. Pepsi does less along that axis — the list above says what is missing — and more along one the others barely have: end-to-end cryptography, the key management that makes it usable, and the administrative surface to operate both. Running Pepsi in front of an existing mail system is therefore a common deployment, and Microsoft Exchange as a gateway covers it.

  • Authentication posture. Modern forwarding authentication — verifying SPF/DKIM/DMARC and re-asserting the result through ARC and SRS — is built into Pepsi’s core flow. On Postfix, sendmail or qmail the same result is assembled from milters, helpers and patches (Exim and Stalwart bring more of it in the box); that is often more work to wire up but draws on mature, widely deployed components.

  • Maturity. This is the decisive difference. Postfix, qmail, sendmail and Exim have decades of production hardening, large operator communities and extensive documentation, and even the much younger Stalwart has seen real deployment. Pepsi has none of that (see the disclaimer above): it is a young, experimental design whose value is in exploring the database-as-queue, pipeline-of-programs approach, not in production maturity.

1.7. How to read this manual

The manual is organised into seven parts, which the table of contents follows in order:

  1. The guide — this chapter, Getting started on a cheap VPS, Installation, Debian packages, The Wizard, Configuration, Operating Pepsi, Troubleshooting, Supported Features and SMTP Protocol Extensions: what Pepsi is, how to get it running (from source or from the Debian packages, by hand or through the setup interview), how to keep it running (logs, monitoring, backups, upgrades) and the protocol- and policy-level feature set.

  2. End-to-end cryptography — Key management (identities, custody, key discovery and publication), The secure-link fallback portal (the portal for a recipient with no key), Client interoperability (which third-party clients have actually been measured against Pepsi’s output), Security model (the threat model all of this is built against) and Microsoft Exchange as a gateway (running Pepsi as a crypto gateway in front of an existing mail system).

  3. Mailing lists — Mailing lists (the GNU Mailman 3 reimplementation), Archives (the HyperKitty-compatible archive) and The GNU Mailman 3 REST API (the Mailman REST API that Postorius and HyperKitty use).

  4. Administration — the The administrative API and the browser The administration console that is its first client.

  5. Internals — Architecture (pipeline, database and dispatcher), The message state (the per-message JSON), Extending the Pipeline (how to add your own program), and Test Suite, Benchmark Suite and Performance.

  6. A reference chapter for every program, followed by the Feature stability table and the manual pages (the man 1/man 5/man 7 reference, reproduced in full).

  7. An index of the standards Pepsi implements — RFC Index — with pointers to where each is realised.

Where to start depends on what you came for:

1.8. Getting help and reporting bugs

The project’s web site is https://pepsi.taler.net/, and the source code is published at https://git.taler.net/pepsi.git.

Please report bugs, and suggest improvements, in the public bug tracker at https://bugs.gnunet.org/view_all_bug_page.php?project_id=35. A useful report names the version (pepsi-setup --version), the relevant part of the configuration (with secrets removed) and what pepsi-status and the service logs say.

A security vulnerability must not go to the public tracker: report it privately by e-mail as described in Reporting a vulnerability.