Skip to content

Envelope

The envelope of a message consists of the sender and the recipients that the client gives in the SMTP MAIL FROM and RCPT TO commands. The envelope decides where the message is delivered and where delivery status notifications are sent. It is independent of the From and To headers that recipients see.

Stalwart can rewrite both parts of the envelope while it receives a message. Rewriting the sender can, for example, replace an internal domain with the public one. Rewriting a recipient can, for example, deliver mail sent to an old address to a new one.

The simplest rewrites are written as expressions, rules that Stalwart evaluates for each address. Two session stages have a rewrite expression:

  • rewrite on MtaStageMail (found in the WebUI under Settings › MTA › Session › MAIL FROM Stage) rewrites the sender, which the expression reads as sender.
  • rewrite on MtaStageRcpt (found in the WebUI under Settings › MTA › Session › RCPT TO Stage) rewrites each recipient, which the expression reads as rcpt.

The expression returns the new address. When it returns false instead, the address is left unchanged.

Matching is usually done with the matches function. Its regular expression captures parts of the address, and the then branch refers to the captured groups as $1, $2 and so on, and to the whole match as $0 (Expression values). The following expression delivers mail for first.last@ addresses in local domains to first+last@:

{
"rewrite": {
"match": {"0": {"if": "is_local_domain(rcpt_domain) && matches('^([^.]+)\\.([^.]+)@(.+)$', rcpt)", "then": "$1 + '+' + $2 + '@' + $3"}},
"else": "false"
}
}

The backslash appears twice because the expression is written inside a JSON document. In the expression field of the WebUI, it is written once, as \. (escaping backslashes). MAIL FROM stage and RCPT TO stage describe the rewrite of each stage in detail.

Some rewrites need more logic than an expression can hold, such as a lookup or several conditions. Such a rewrite is written as a system Sieve script at the MAIL FROM or RCPT TO stage, and the stage’s script field selects it. The script reads the envelope with the envelope test. To replace an address, it sets the envelope.from or the envelope.to variable.

The following script runs at the RCPT TO stage and removes the dots from the local part of a recipient. With it, mail for [email protected] is delivered to [email protected]:

require ["variables", "envelope", "regex"];
if allof( envelope :localpart :contains "to" ".",
envelope :regex "to" "(.+)@(.+)$") {
set :replace "." "" "to" "${1}";
set "envelope.to" "${to}@${2}";
}

Try this script in Sievepad

The script works in three steps. The envelope :regex test captures the local part and the domain of the recipient. set :replace then removes the dots from the local part. Finally, setting envelope.to replaces the recipient with the new address.

The same mechanism can also change the delivery status notifications that a recipient produces, by setting envelope.notify. Delivery status notifications shows how to suppress them this way.