1. Introduction

1.1. What Pepsi is

Pepsi is an e-mail forwarding pipeline: it receives mail on its own SMTP server, authenticates it, and re-sends it on to the real destinations. Unlike a classical store-and-forward MTA, Pepsi is built as a series of small, single-purpose stage programs chained over one shared database table, so that each processing step — ARC sealing, SRS rewriting, DKIM signing, bounce generation, delivery — is an independent program that can be reordered, replaced or extended.

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.

The result is a forwarder that interoperates cleanly with modern receivers.

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’s answer is to move 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 to do that with 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. No administrator can read a stored message.

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.ingress 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, requeuing the message for the next stage as it advances.

  3. The pepsi-stage-* programs are the stages: ARC, SRS, DKIM signing, encryption and decryption, bounce generation, alias expansion, per-recipient routing and policy filters (whitelisting, pay-to-send, language and per-address settings), 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, a Prometheus /metrics page, the Web Key Directory endpoints that publish our users’ keys, the The secure-link fallback portal portal, and — on listeners explicitly flagged for it — 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. Key features

  • Authentication-preserving forwarding. SPF, DKIM and DMARC are verified on arrival and the verdict is sealed into an ARC chain (RFC 8617), so the final receiver can trust what Pepsi saw even though forwarding broke the original signals.

  • Envelope repair. SRS (Sender Rewriting Scheme) 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 signs outgoing mail with the From: author’s key and encrypts it per recipient in OpenPGP (PGP/MIME, RFC 3156) or S/MIME (CMS, RFC 8551); pepsi-stage-decrypt opens and verifies inbound ciphertext and records the verdict. Users get end-to-end cryptography without a client plugin. 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 — at the operator’s choice — a key server.

  • A secure-link portal for correspondents with no key. Rather than choosing between cleartext and a bounce, The secure-link fallback portal stores the message encrypted under a freshly generated PIN and mails the recipient a link. No administrator can read a stored message.

  • 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, 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.

  • Flexible delivery. 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. It has not been through a stable release, its on-disk and database formats may change without migration paths, 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.

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 — clarifies the trade-offs Pepsi makes. The aim here is to be informative rather than to argue that any one approach is best; 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. This makes routing and policy decisions very flexible without external helper programs, at the cost of a configuration that is powerful but 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 small pipeline that only forwards.

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. Pepsi is still not a general-purpose MTA — it is built to forward mail correctly in the era of SPF/DKIM/DMARC, with local delivery as a secondary capability — but it is also something none of the systems above is: 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. That is the axis on which it differs most from the others, and it is why it is worth running in front of one of them rather than instead of one.

1.6.2. At a glance

The table below summarises where these designs differ. The cells are necessarily terse — “built-in” versus “via add-ons” hides a great deal of nuance — but it shows the broad 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)

Forwarding pipeline (+ optional local Maildir)

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, pre-release

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

SPF/DKIM/DMARC/ARC

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)

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

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, whitelist, language filter; milters (post-queue) for RBL/AV/greylisting

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)

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)

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)

Concretely, a full MTA such as Postfix, Exim or sendmail provides a number of capabilities that Pepsi deliberately does not offer today:

  • 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. What it does instead is hand 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. The one user-filtering action Pepsi does implement 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 gate 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.

  • A mailing-list manager. Alias expansion (pepsi-stage-aliases) fans one address out to many, but there is no subscription handling, no archive and no bounce processing of the kind a list manager provides.

Two entries that used to be on this list have come off it, and are worth naming so an older reading of this chapter is not carried forward. 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).

One capability is not on that list, although the table above tracks it: mailbox access via POP3 or IMAP. That is deliberate — serving mailboxes to end-user clients is not an MTA function. Postfix, qmail, sendmail and Exim do not provide it either; a site running any of them pairs it with a separate server such as Dovecot, and a Pepsi deployment does the same. The only system compared here that bundles mailbox access is the all-in-one Stalwart, so its absence distinguishes Pepsi from Stalwart, not from the traditional MTAs.

If anything Pepsi is set up for that pairing more directly than the others. pepsi-stage-relay-to-lmtp hands messages to an MDA 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 implements no Sieve of its own. 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. What Pepsi does not do is serve those mailboxes to a client, and it has no plans to.

These are scope decisions, not bugs: Pepsi trades the breadth of a full MTA for a small, auditable pipeline aimed squarely at authentication-preserving forwarding.

1.6.3. 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 makes the 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.4. 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 has proven to combine 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.5. Scope and maturity

  • Scope. Postfix, qmail, sendmail and Exim are complete, general-purpose MTAs that can run a site’s entire mail system, and Stalwart goes further still by bundling mailbox access. Pepsi deliberately does less along that axis: it is a forwarder (plus optional local delivery), so there is far less to configure for that niche, but it is not a drop-in replacement for a full MTA and lacks many features a general MTA is expected to have. It does more along one axis the others barely have: end-to-end cryptography, the key management that makes it usable, and the administrative surface to operate both. A common deployment is therefore Pepsi in front of an existing mail system rather than in place of it — see Microsoft Exchange as a gateway.

  • 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 yet (see the disclaimer above): it is a young, experimental design whose value today is in exploring the database-as-queue, pipeline-of-programs approach to authentication-preserving forwarding — not in production maturity.

1.7. How to read this manual

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

  1. The guide — this chapter, Getting started on a cheap VPS, Installation, Configuration, Supported Features and SMTP Protocol Extensions: what Pepsi is, how to get it running, and the protocol- and policy-level feature set.

  2. End-to-end cryptographyKey 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) and Microsoft Exchange as a gateway (running Pepsi as a crypto gateway in front of an existing mail system).

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

  4. InternalsArchitecture (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.

  5. 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).

  6. 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: