8. SMTP Protocol Extensions¶
Beyond the standardised ESMTP service extensions catalogued in Supported Features and RFC Index, Pepsi defines two message header fields of its own that extend the e-mail protocol with Pepsi-specific semantics. Neither is an IANA- or RFC-registered field; both are private extensions that only a Pepsi deployment (and, for the payment header, a GNU-Taler-aware wallet) interprets, and both are designed to pass transparently through intermediate MTAs that do not understand them.
This chapter is the normative reference for those two fields:
the
Pepsi-Origin:proof-of-origin header, stamped on outbound mail; andthe
Taler:payment header, which advertises a pay-to-send payment request to a wallet.
Both are optional fields in the sense of RFC 5322 §3.6.8: a field name of printable US-ASCII followed by an unstructured value, which every conforming MTA relays untouched and every conforming reader ignores if it does not recognise it. That is what makes a private extension safe to deploy.
Neither is registered with IANA under RFC 3864, and neither carries the X-
prefix — deliberately. RFC 6648 deprecates X- precisely because a field that
succeeds has to be renamed, breaking every implementation that adopted it early.
For Pepsi-Origin: there is a second, sharper reason: pepsi-stage-decrypt
strips the whole X-Pepsi-* namespace off inbound mail so a remote sender
cannot forge a verdict, and a proof-of-origin header inside that namespace would
be stripped along with it — exactly on the returning bounce where it is needed.
8.1. The Pepsi-Origin: proof-of-origin header¶
When Pepsi relays an outbound message off-site it stamps it with a
Pepsi-Origin: header that cryptographically proves this deployment
originated the message and records which of its authorised senders did. The
goal is to let a returning bounce be trusted: a DSN that comes back days later
embeds (a copy of) the original message — and therefore the Pepsi-Origin:
header — inside its report body, so pepsi-stage-anti-spam can
recover that header and confirm the bounce is a reply to mail Pepsi really sent,
rather than backscatter or a forged bounce aimed at the pay-to-send gate.
8.1.1. Field syntax¶
The header value is a list of key=value fields separated by ;:
Pepsi-Origin: v=1; host=mail.example.org; sender=alice@example.org;
nonce=<base64url>; ts=<epoch-seconds>; mac=<base64url>
Field |
Meaning |
|---|---|
|
Format version; a verifier accepts only |
|
The originating deployment’s hostname ( |
|
The authorised envelope-sender account the message originated from. |
|
A random 128-bit nonce, base64url-encoded (RFC 4648, no padding). |
|
The Unix timestamp (seconds) at which the header was minted. |
|
|
The MAC covers the version, hostname, sender account, nonce and timestamp, each
field length-prefixed with its big-endian u32 length and concatenated, so no
field boundary can be shifted. The hostname is both carried in the clear and
folded into the MAC: the clear copy lets a verifier reject a bounce minted for a
different Pepsi deployment cheaply, before any database lookup, while binding it
into the MAC means a captured header cannot be replayed against a sibling
deployment that happens to share a secret. Because only Pepsi ever generates or
verifies these headers, the wire format does not need to interoperate with
anything.
8.1.2. Minting and verification¶
Stamping. Both relay stages — pepsi-stage-relay-to-internet and pepsi-stage-relay-to-smarthost — call the shared
pepsi-common::originengine to prepend the header on whichever outbound path a message takes, and record the freshly minted nonce in thepepsi.origin_noncetable with a two-week expiry. Stamping is best-effort: if the feature is off the message is unchanged, and a failure to record the nonce is logged but never blocks delivery.Verification. pepsi-stage-anti-spam, on a returning bounce, scans the whole message (the header sits inside the DSN body, not the bounce’s own header block), unfolds any continuation lines, and accepts the first header whose version and hostname match and whose MAC verifies under the secret. Only if the cheap cryptographic check passes is the (slower)
pepsi.origin_noncelookup consulted; a bounce verifies only if its nonce is still tracked. A bounce that does not verify — a missing/forged/expired header, or any bounce when proof-of-origin is not configured — is routed to the stage’sBOUNCE_TARGET_STAGE(e.g. a quarantine or discard stage) rather than relayed back to a sender; a genuine bounce is forwarded normally. A bounce is never silently dropped.
8.1.3. Configuration¶
Keying lives in the shared [pepsi-origin] section (SECRET / SECRET_FILE,
plus the required [pepsi-ingress] HOSTNAME that is bound into every MAC). The
feature is on by default: when no secret is configured, pepsi-setup
provisions a random one on first run. The secret must stay stable over time (a
bounce returned days later must still verify) and be identical across all
instances of a deployment. Removing the section disables the feature — outbound
mail is then unstamped and every bounce is unverifiable. See
pepsi-stage-anti-spam and pepsi.conf(5).
8.2. The Taler: payment header¶
When pepsi-stage-anti-spam holds a message for payment it
originates a payment-request auto-reply to the sender (Auto-Submitted:
auto-replied per RFC 3834, null envelope sender). That reply carries the GNU
Taler payment URI
Taler: taler://pay/<backend>/<order-id>/
as a header field, and in its body as a clickable link and an inline cid:
PNG QR code (RFC 2392 for the cid: reference), together with a pointer to
https://wallet.taler.net/ for senders without a wallet. The header is added
alongside the body copy, never instead of it, so a human reader and a non-Taler
client are wholly unaffected.
The value is exactly the URI the stage mints, with order_id == token, so a
wallet resolves the order against the merchant backend just as it would from the
body link. taler:// is not an IANA-registered URI scheme (RFC 7595 sets the
procedure); it is GNU Taler’s own, and the wire form is documented by that
project rather than here.
8.2.1. Why a header and not only the body¶
The header is what makes the demand machine-detectable, and Pepsi is itself
the machine that reads it. pepsi-stage-auto-pay — the
sending-side counterpart, run on the inbound path — recognises a returning
payment demand by this field: it unfolds continuation lines, accepts several
URIs in one field and several ``Taler:`` fields (a message to a list can draw
more than one demand), and ignores taler:// URIs of any other kind so only
genuine pay demands are ever settled.
Detection alone is not authorisation. The stage pays only for mail this
deployment provably originated, which it establishes with the
Pepsi-Origin: header described above — recovered from the returned message
and checked against pepsi.origin_nonce. The two extensions in this chapter
are therefore a pair: one proves origin, the other states the price, and the
budget is booked against the origin nonce so a single original message cannot be
charged twice.
The reply also copies the original message’s Pepsi-Origin: header verbatim,
which is what lets the correlation survive the round trip.
8.2.2. Interaction with DKIM and ARC¶
The auto-reply is injected at BLOCK_RESPONSE_STAGE, so it is DKIM-signed on
the way out like any other message. Whether the Taler: field is covered by
that signature follows the stage’s ordinary h= configuration
(pepsi-stage-dkim-sign); it is not special-cased. Because
DKIM header selection is bottom-up (RFC 6376 §5.4.2), adding the field does not
disturb any signature already present.
8.3. See also¶
pepsi-stage-anti-spam, pepsi-stage-auto-pay, pepsi-stage-relay-to-internet, pepsi-stage-relay-to-smarthost, Supported Features, RFC Index.