10. 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:
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.
Neither is registered with IANA under RFC 3864. Both are private extensions that only a Pepsi deployment (and, for the payment header, a GNU-Taler-aware wallet) interprets, and 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.
Pepsi names its header fields by what they are for. The two protocol fields
above travel between deployments and carry no X- prefix: RFC 6648 deprecates
X- because a field that succeeds has to be renamed, breaking every
implementation that adopted it early.
Fields that only annotate a message for its local recipient or carry a request
from a local mail client — X-Pepsi-Crypto, X-Pepsi-Detected-Languages,
X-Pepsi-Sign and the rest — share the X-Pepsi-* namespace, which is
never meant to reach another deployment. pepsi-stage-decrypt
strips that whole namespace off inbound mail, so no remote sender can forge a
local verdict. The same rule is why Pepsi-Origin: must stay outside it: a
proof of origin in the namespace would be stripped exactly on the returning
bounce where it is needed. The local fields are documented with the stage that
writes or reads them, not here.
10.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. That
lets 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.
10.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.
10.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) when one is wired, and otherwise forwarded toNEXT_STAGEunchanged; a genuine bounce is always forwarded normally. A bounce is never silently dropped, and never gated on payment.
10.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.
To switch the feature off, set ENABLED = no in [pepsi-origin]. Outbound
mail is then unstamped, every bounce is unverifiable (routed per the anti-spam
stage’s BOUNCE_TARGET_STAGE, else forwarded), and pepsi-setup mints no
secret; an existing secrets.d/pepsi-origin.secret is left in place, so
switching it back on resumes with the same key. Removing the section is not an
off switch: the next pepsi-setup run would find no secret and provision a new
one. See pepsi-stage-anti-spam and pepsi.conf(5).
10.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.
10.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 budget is booked against that 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.
10.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
(SIGNED_HEADERS, see pepsi-stage-dkim-sign), whose default
does not include it; 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.
10.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.