Stalwart Mail Server: How to Automatically Archive SMTP Outgoing Mail Using Sieve Scripts
If you are using the modern Stalwart Mail Server, you may have noticed a classic email architecture pain point:
When you send emails using clients like Thunderbird or Outlook, the sent emails dutifully reside in the “Sent” folder; however, when you configure notification bots (e.g., notification@example.com) or ERP systems to send emails automatically via the SMTP protocol, the outbox is empty.
Why is this happening? This is because the standard SMTP protocol is only responsible for “transmission,” not “storage.” Desktop clients are able to save backups because they perform an additional IMAP upload operation after sending. Simple SMTP scripts usually do not perform this step.
To solve this problem, we need to intercept and archive at the server level. This article will introduce the most stable and RFC-compliant solution—the Loopback Sub-addressing Strategy.
Core Principle: Loopback Sub-addressing
In Stalwart, directly using fileinto to write files during the sending phase often leads to failure or unpredictable consequences due to context permission issues.
Therefore, we adopt a smarter “two-stage” strategy:
- Outbound Interception (System-level): When a specific user sends an email via SMTP, a system script intercepts it and generates a copy, redirecting it back to the sender, but with a special tag (e.g.,
user+sent@domain). - Inbound Archiving (User-level): When this copy with the
+senttag enters the inbox process, the user’s personal script recognizes it, marks it as read, and moves it to the “Sent” folder.
This method decouples sending and storage logic, making it stable and secure.
Step One: Configure System-level Outbound Interception Script
We need to add a Trusted Script to Stalwart’s config.toml, mounted in the DATA stage of SMTP. Only at this stage can the complete email content be obtained.
1. Write the Script Logic
Add a script in the Web interface by clicking Settings -> Scripting -> System Scripts -> Create script. The core task of this script is authentication and deadlock prevention. The name and description can be filled in as desired. In the following text, the name save-sent will be used as an example.
require ["variables", "envelope", "copy", "subaddress"];
# --- Configuration Area ---
# Define the accounts to be monitored
set "monitor_user" "notification@example.com";
# Define the sub-address tag for identification
set "sent_tag" "sent";
# --- Core Logic ---
# 1. Security check: Verify the authenticated user of the current SMTP session
# This is crucial! We only process sessions authenticated as "notification" by password.
# If a hacker forges the From header but is not authenticated, this script will not execute, ensuring the authenticity of the audit.
if string :is "${env.authenticated_as}" "${monitor_user}" {
# 2. Loop Prevention Check
# Check if this email is already a copy we generated (i.e., check if the recipient contains +sent)
# Without this step, copies would trigger redirection indefinitely, leading to server crash.
if not envelope :detail :is "to" "${sent_tag}" {
# 3. Perform redirection with copy
# :copy ensures the original email is sent to external recipients normally
# Send a copy back to oneself, with the +sent tag
redirect :copy "notification+sent@example.com";
}
}Technical Details: Here, the
${env.authenticated_as}variable is used instead of checking theFromheader to prevent sender spoofing and ensure that only legitimate sends through authentication trigger archiving.
2. Enable the Script
In the Settings interface, click SMTP -> Inbound -> DATA stage, fill in "save-sent" in Run Script, then click Save & Reload to save and load the configuration.
Step Two: Configure User-level Inbound Archiving Script
Now, the server will automatically send an email notification+sent@example.com to itself. We need to tell Stalwart how to handle this “boomerang” email.
This falls under personal Sieve filtering rules. You can upload it through a ManageSieve-supported client, or an administrator can configure it directly.
User Script Content:
require ["fileinto", "mailbox", "subaddress", "imap4flags", "envelope"];
# Check if the email contains the +sent tag
if envelope :detail :is "to" "sent" {
# 1. Mark as read
# Sent emails should logically be in a "read" state (\Seen)
setflag "\\Seen";
# 2. File into the "Sent Items" folder
# :create ensures that the folder is created if it doesn't exist
# Note: Depending on your client's habits, this might be "Sent", "Sent Items", or "Sent Messages"
fileinto :create "Sent Items";
# 3. Stop processing
# Prevent this email from matching further and mistakenly entering the inbox
stop;
}Step Three: Check Environment and Activation
Before applying the above configuration, please ensure your Stalwart environment meets the following two basic conditions:
- Enable Sub-addressing: This is crucial for
notification+sentto be recognized. It is enabled by default and can be confirmed via Settings -> SMTP -> RCPT stage -> Sub-addressing. - Confirm Folder Name: Different clients name the sent folder differently. It is recommended to hardcode the name you primarily use in the script. Stalwart defaults to
Sent Items, which can be confirmed via Settings -> Message Store -> Default Folders -> Sent.
Summary
Through this “Loopback Sub-addressing” solution, we not only perfectly solved the problem of SMTP outgoing mail archiving in Stalwart but also ensured high security (based on authenticated identity) and stability (based on RFC standards). This is the current best practice for automated notification systems that require audit trails.