Cabalmail

Host your own email and enhance your privacy

View the Project on GitHub cabalmail/cabal-infra

Mail rules

Users can author an ordered list of rules that the IMAP tier applies to every arriving message before it reaches the inbox: file it into a folder, flag it, mark it read, forward it, answer it with an auto-reply, or discard it. Rules are composed in a structured editor in the native clients, stored server-side, and compiled into per-user procmail on the imap tier — user input never becomes procmail syntax directly.

This page is the user-facing reference (what rules can and cannot do) plus enough operator context to debug a delivery. The security model, the compiler design, and the escaping rules live with the code in docker/shared/compile-user-rules.py.

Editing rules

The editor lives in each native client’s Settings:

All clients edit the same server-side list. Changes auto-save about a third of a second after you stop editing; the indicator at the bottom of the list shows saving / saved / error states. If two devices edit at once, the second save is rejected and that device offers a single Reload — last writer wins, unsaved edits on the loser are discarded. There is no merge.

The list shows one row per rule with an enable switch and a one-line summary. Rules can be added (blank or from a starter template), duplicated, deleted, and reordered; a disabled rule is kept but never evaluated. To reorder on iPhone, iPad and Vision Pro, tap Edit and drag, or choose Move Up or Move Down from a rule’s long-press menu. On the Mac, choose Move Up or Move Down from a rule’s ⋯ menu or its right-click menu; Android has the same items in each rule’s menu.

Conditions

A rule has zero or more conditions, ANDed together. Each condition is a field plus a case-insensitive contains match on that field. A rule with no conditions matches every message — that is how a vacation reply is built, and also how mail is accidentally mass-filed, so watch the summary line.

Field What is searched
From The From: header line, display name included
To The To: header line
Cc The Cc: header line
Subject The Subject: header line
Body The message body

Because matching runs against the raw header line, a condition value of aws.amazon.com matches any From address at that domain, and a value of Jane Smith matches a display name. There are no regular expressions, no OR, and no NOT — the value is always a literal substring.

There is no BCC field, deliberately. Blind-carbon-copy recipients are removed from the headers before a message is transmitted — that is what makes them blind — so by the time a message arrives there is nothing for a BCC condition to match. A control that silently never fires would only mislead, so it is not offered.

Actions

Each rule picks at most one destination and any combination of extras.

Destinations (mutually exclusive):

Move, Archive, and Delete are only offered on rules that end processing. While Continue to the next rule is on, the destination choices narrow to Copy and None — a rule that continues passes the message along, so any delivery it makes is necessarily a copy, and the editor says so instead of letting a “Move” quietly behave as one. Turning Continue on while Move or Archive is selected converts the destination to Copy, carrying the folder; the delivered mail is identical either way, only the label is more honest. (Rules saved as Move or Archive with Continue on by older clients are likewise shown and saved as Copy.)

Folder pickers offer only folders that already exist. The system never creates a folder on your behalf — not when you save a rule and not at delivery time. If a rule’s destination folder is later deleted, the rule is skipped (the message stays in the inbox) until you pick another folder; the editor flags the missing folder. Archive is not special-cased: with no Archive folder an Archive rule is skipped too, which is why the editor offers an explicit “Create Archive folder” button when you pick that action.

Extras (independent; all disabled when the destination is Delete — there is no message left to act on):

Auto-replies

Reply is the vacation-responder: a plain-text body you write, sent back automatically to whoever mailed you.

Order and spill-through

Rules run top to bottom in the order you arrange them. By default the first rule whose conditions match is the last one that runs for that message. Turning on a rule’s Continue to the next rule lets evaluation keep going after it fires, so later rules also see the message. A Delete rule never continues (there is nothing left to evaluate).

A continuing rule never settles the message’s fate: it passes the message along, and any delivery it makes is a copy (which is why its destination can only be Copy or None). The inbox is the fallback destination — a message that runs off the end of the list without a rule ending processing for it is delivered to the inbox, copies and decorations intact. The editor refuses a continuing rule that neither files, flags, marks read, forwards, nor replies; it would have no effect at all.

Flag, custom flags, and Mark-as-read on a continuing rule with destination None decorate the message: the marks are carried along and applied wherever the message ends up — a later rule’s folder, or the inbox fallback. That is how “file receipts into Receipts AND flag anything from billing” composes from two rules, decorators above filers:

  1. From contains billing → destination None, Flag, Continue on.
  2. Subject contains receipt → Move to Receipts.

A billing receipt matches rule 1, picks up the flag, continues, and rule 2 files it — one flagged message in Receipts, nothing extra in the inbox. A billing message that is not a receipt runs off the end and lands in the inbox, flagged. Order matters: a decorator below the filer never sees a receipt (the filer ends processing first).

On a rule that itself delivers — a Move, an Archive, a Copy, or a terminal None — Flag and Mark-as-read apply to that rule’s own deliveries (plus any decorations picked up earlier), as they always have.

Limits

Limit Value
Rules per account 100
Conditions per rule 10
Condition value length 500 characters
Forward addresses per rule 10
Copy destinations per rule 10
Reply body length 4000 characters
Auto-replies per account per 24h 100
Auto-replies per sender 1 per 7 days

When changes take effect

Saving publishes a change notification that the imap tier picks up within seconds; a periodic fallback recompiles every ~15 minutes even if the notification is lost. Rules apply only to mail that arrives after compilation — they never re-run against mail already delivered, and there is no retroactive “apply to existing messages”.

Why didn’t my rule fire?

Work down this list:

  1. Is the rule enabled? Disabled rules are skipped entirely.
  2. Did an earlier rule already match? Without spill-through, the first matching rule wins. Check the rules above it in the list.
  3. Does the destination folder still exist? A rule whose folder was deleted is skipped and the message stays in the inbox; the editor marks the folder as missing.
  4. Are the rule’s custom flags still in your palette? A rule that sets a flag you have since deleted or disabled is skipped entirely until you update it (Settings → Flags shows the palette).
  5. Is the condition matching what you think? Matching is a literal substring against the raw header line. To contains matches the To: header — not the envelope recipient — so mail where your address only appears as a BCC will not match a To condition.
  6. Had the change propagated yet? A rule saved moments before a message arrived may not have compiled in time; anything after the ~15-minute worst case is in effect.
  7. For replies: the same sender within 7 days, bulk/list/auto- submitted mail, and anything past the daily cap are all silently suppressed by design.
  8. Still stuck? An operator can check the compile log (below) for a compile_skip_rule line naming your rule and the reason.

Operator notes: where rules live

Users see none of the above; their contract is the editor plus this page’s behavior descriptions.