Sieve filters and vacation replies

Personal mail rules with Sieve, every supported Sieve extension and limit, managing scripts over ManageSieve, and out-of-office replies set over JMAP or Sieve.

Nixt Server runs each person’s own rules on the server, as mail is delivered, so they work whichever app — or no app — is open. Rules are written in Sieve, the standard language for mail filters, and managed over ManageSieve. Out-of-office replies can also be set from a JMAP app without writing any Sieve.

How rules run

  • Each account can keep up to 32 Sieve scripts. One of them is active, and only the active script runs.
  • The active script runs once for every message delivered to the account, after the server’s own spam and malware filtering has accepted it.
  • Mail the filter tagged as junk or quarantined goes to Junk or Quarantine whatever the script says; the script’s other actions, such as flags and notifications, still apply.
  • If the script does nothing that files or discards the message, the message is kept in Inbox.
  • If the script fails while running — it goes over a limit, for example — the message is kept in Inbox and the failure is written to the server’s log.
  • Scripts are checked when they are uploaded, so a script with a mistake is refused with the reason and its position rather than failing later.

A first script

require ["fileinto", "imap4flags", "vacation"];

# File mailing-list mail into its own folder, creating it if needed
if header :contains "list-id" "announce.example.org" {
    fileinto :create "Lists/Announce";
    stop;
}

# Flag mail from the boss
if address :is "from" "sam@example.com" {
    addflag "\\Flagged";
}

# Out of office
vacation :days 7 :subject "Away until Monday" "I am away and will reply when I am back.";

Upload it with any ManageSieve app, then make it active. See ManageSieve.

Supported extensions

Every script may require any of these. The ManageSieve SIEVE capability lists them, together with the comparators i;octet and i;ascii-casemap.

ExtensionWhat it addsStandard
envelopeTest the envelope sender and recipient.RFC 5228
fileintoFile into a folder.RFC 5228
encoded-characterWrite characters as ${hex:…} and ${unicode:…} in strings.RFC 5228
comparator-i;ascii-numericCompare strings as numbers.RFC 4790
vacationAutomatic replies.RFC 5230
vacation-secondsvacation :seconds, for reply periods shorter than a day.RFC 6131
variablesset, variables and match variables such as ${1}.RFC 5229
relational:count and :value with gt, lt and the rest.RFC 5231
imap4flagsSet, add and remove flags and keywords.RFC 5232
subaddressTest :user and :detail in user+detail@example.com.RFC 5233
bodyTest the message body.RFC 5173
copy:copy on fileinto and redirect, keeping the message where it would have gone.RFC 3894
editheaderaddheader and deleteheader.RFC 5293
includeRun another of your scripts from this one.RFC 6609
dateTest dates in headers and the current date.RFC 5260
indexTest the first, second or last occurrence of a header.RFC 5260
extlistsTest against named external lists. No lists are defined, so these tests never match.RFC 6134
enotifySend a notification, with the mailto method.RFC 5435, RFC 5436
ihaveTest whether an extension is available.RFC 5463
mailboxfileinto :create, and test whether a folder exists.RFC 5490
environmentTest facts about the environment.RFC 5183
rejectRefuse a message with a reason sent back to the sender.RFC 5429
erejectThe same, for protocols that can refuse during delivery.RFC 5429
duplicateTest whether this message has been seen before.RFC 7352
fccKeep a copy of a vacation reply or notification in a folder.RFC 8580
special-useFile into a folder by its use, such as \Junk, whatever it is called.RFC 8579
mailboxidFile into a folder by its id, which survives renaming.RFC 9042
regex:regex matching.draft-ietf-sieve-regex

Notes on some extensions

  • Folder names. fileinto "Receipts" files into a folder of that name. If it does not exist and :create is not given, the message goes to Inbox. fileinto :specialuse "\\Archive" "Archive" finds the folder by use first.
  • Special uses available: \Drafts, \Sent, \Junk, \Trash and \Archive.
  • redirect sends the message on to another address. A destination outside your organisation goes only once your organisation allows it: saving the script answers OK (WARNINGS) saying the forward waits for approval, and until then the message is kept in your mailbox (see Forwarding outside the organisation). The server rewrites the envelope sender so the destination’s SPF check sees this server’s domain. A redirect to the account’s own address is ignored. Without :copy, the message is not kept.
  • reject and ereject keep the message out of the mailbox and send the sender a delivery failure notice carrying your reason, with status 550 5.7.1, when the sender asked for failure notices and passed SPF or DKIM.
  • notify with a mailto: URI sends a short notification message from your address. A :from that is not one of your own addresses is set aside, and the notification comes from you, because the server signs the mail it sends in your domain’s name.
  • include can include your own scripts (:personal).
  • environment answers name, version, location and phase.
  • date and currentdate use UTC when the script names no zone.
  • duplicate remembers what it has seen for up to 30 days.

Limits

LimitValue
Scripts per account32
Size of all an account’s scripts together1 MiB
Size of one script64 KiB
Script name1 to 128 characters; no control characters, / or \; no leading or trailing blanks
Nesting of blocks and tests32
One string, literal or expanded64 KiB
Variables a script may set128
Length of one variable’s value8 KiB; longer values are cut
Commands and tests evaluated for one message100,000
Characters compared by :matches, :contains and :regex for one message50 million
redirect actions per run4 (advertised as MAXREDIRECTS)
fileinto and redirect actions together32
notify actions per run8
include depth, and includes per run8 deep, 32 in all
addheader and deleteheader actions per run32
Header fields read from a message1,000
Compiled size of one :regex pattern256 KiB
vacation :days1 to 30, default 7
vacation :seconds0 to 30 days
duplicate :secondsUp to 30 days

Limits the organisation sets

An organisation can set tighter limits on its people’s scripts in its runtime settings:

SettingWhat it limits
sieve.redirects_per_messagePlaces one message may be redirected to by a person’s script.
sieve.redirects_per_dayMessages one person’s scripts may redirect in a day. Past it, the redirect is held and the message is kept.
sieve.vacations_per_dayAutomatic replies one person may send in a day, from a script or from the out-of-office setting.
sieve.script_bytesHow large a script may be.
sieve.stepsCommands and tests one run of a script may evaluate.

Each refusal names the setting that caused it in the message’s trace.

The organisation’s own rules

An organisation can run a Sieve script of its own before everybody’s and another after:

  • Before runs first, on every message to the organisation’s people. What it decides stands: a discard, a reject or a stop there ends the run, and nothing a person’s own script does takes back what it filed.
  • A person’s own script runs next, as ever.
  • After runs last, on every message — one a person’s script rejected too — which is where an archive or journal copy goes. What it files stands, even over a person’s discard.

Each script sees the message as the one before left it, headers it edited included. A script that fails to run counts as having done nothing, so mail is never lost. The organisation’s redirects are its own forwards: they never wait for its approval of forwarding, and never count against a person’s limits.

For example, to keep a copy of every message in an archive:

require "copy";
redirect :copy "vault@archive.example";

Organisation administrators set them, and auditors read them:

  • Console: Settings › Organisation rules.

  • Command line:

    versealx-server admin scripts show
    versealx-server admin scripts set --after after.sieve
    versealx-server admin scripts set --no-after
  • API: GET and PUT /api/v1/tenants/{tenant}/scripts, both saved whole with the version read.

A script that doesn’t compile is refused, naming the line and the column.

ManageSieve

ManageSieve is the protocol apps use to upload and choose scripts. Connect to the server’s host name on port 4190, start TLS with STARTTLS, and sign in with your email address and password.

CommandWhat it does
CAPABILITYLists IMPLEMENTATION, VERSION 1.0, SIEVE (the extensions), SASL (once TLS is on), STARTTLS (before it is), NOTIFY mailto, MAXREDIRECTS 4 and UNAUTHENTICATE.
STARTTLSStarts TLS. Required before signing in.
AUTHENTICATESigns in. See Signing in.
LISTSCRIPTSLists your scripts, marking the active one ACTIVE.
PUTSCRIPT <name> <script>Uploads or replaces a script. The script is checked first and refused with the reason if it does not compile.
CHECKSCRIPT <script>Checks a script without saving it.
GETSCRIPT <name>Downloads a script.
SETACTIVE <name>Makes a script the active one. SETACTIVE "" turns all scripts off.
RENAMESCRIPT <old> <new>Renames a script.
DELETESCRIPT <name>Deletes a script. The active script cannot be deleted; make another active, or none, first.
HAVESPACE <name> <size>Asks whether a script of that size would fit.
NOOPKeeps the connection alive.
UNAUTHENTICATESigns out without closing the connection.
LOGOUTEnds the session.

Refusals are NO replies. The response code in brackets is what an app acts on; the text is what it can show you.

CodeTextCause
—Authenticate firstA script command before signing in.
ENCRYPT-NEEDEDUse STARTTLS before authenticatingSigning in without TLS.
NONEXISTENTno script named "<name>"No script has that name.
ACTIVE"<name>" is the active scriptDeleting the active script.
ALREADYEXISTSa script named "<name>" existsRenaming onto an existing name.
QUOTA/MAXSCRIPTSthe account may keep 32 scriptsThe account already has 32 scripts.
QUOTA/MAXSIZE<n> bytes of scripts would exceed the quota of 1048576The scripts together would be over 1 MiB, or one upload is too large.
—a script name is 1 to 128 charactersAn empty or overlong name.
—a script name cannot start or end with a blankSpaces around the name.
—The position and reasonThe script does not compile.
TRYLATER—The store did not answer; try again.

After several failed sign-ins on one connection, the server sends BYE "Too many failed authentications" and closes it.

Vacation replies

An out-of-office reply can come from two places:

Set withWhereBest for
JMAP VacationResponseA JMAP app such as Nixt Mail.People who want a switch, dates and a message, without writing Sieve.
A Sieve vacation actionYour active script.Replies that depend on who wrote, or what about.

If the active script runs a vacation action for a message, that reply is used and the JMAP setting is ignored for that message.

Setting a reply over JMAP

VacationResponse/set on the singleton object takes:

PropertyMeaning
isEnabledWhether to reply at all.
fromDateDo not reply before this time. Leave empty to start now.
toDateStop replying at this time. Leave empty to reply until turned off.
subjectThe reply’s subject. Empty means Auto: followed by the original subject.
textBodyThe reply’s text.

A JMAP reply goes to each sender at most once every 7 days. Nixt Mail’s out-of-office setting uses this; see the Nixt Mail documentation at /docs/mail.

Who gets a reply

For both kinds of reply, a message gets none when:

RuleWhy
The envelope sender is emptyIt is a bounce or a notification.
It has a List-Id, List-Unsubscribe or List-Post header, or Precedence: bulk, list or junkIt came from a mailing list or a bulk sender.
It has an Auto-Submitted header other than noIt was sent automatically.
None of your addresses is in To or CcIt reached you as a copy, through a list, or through an alias. A Sieve vacation :addresses can name more addresses.
It is from yourselfReplying to yourself helps nobody.
The sender already had a reply within the periodOne reply per sender per period.
The filter put it in Junk or held it in quarantineSpam’s sender is usually forged, so a reply would go to somebody who never wrote.
You, your domain or your organisation block the senderA reply would tell them your address is read. See Allowed and blocked senders.

What the reply looks like

  • From is your address, or the Sieve :from address when it is one of your own; any other :from is set aside. To is the sender.
  • It is signed with your domain’s DKIM keys, so it passes DMARC where it lands. See Mail the server writes itself.
  • Subject is the one you set, or Auto: and the original subject. A subject that is not plain ASCII is encoded so every mail app displays it correctly.
  • In-Reply-To and References link it to the original, so it appears in the same conversation.
  • Auto-Submitted: auto-replied and Precedence: bulk stop other servers’ auto-responders replying back.
  • The envelope sender is empty, so the reply can never itself cause a bounce loop.

Something unclear or out of date on this page? Tell us.