Skip to content
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ

Rules, Sieve & routing

4 Topics 4 Posts

Sieve (RFC 5228) and ManageSieve, the classic rules engine, global rules, SMTP routes, aliases and distribution lists — sorting and steering mail without code.

  • 0 Votes
    1 Posts
    133 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) holds a rule and a Sieve script to their owner: a domain's or an account's rule may no longer set From, Sender, Resent-From, Resent-Sender, DKIM-Signature or the loop count, and its Reply goes only from an address its owner may send as; a Sieve script may no longer add or delete those fields, Auto-Submitted or the loop count, and its notify goes from the account's own address unless its :from reaches the account. One stored before the upgrade still runs without that action or edit, and the application log names it. A loop count that is not digits alone, or is above the limit, reads as the limit, and RuleLoopLimit 0 now stops out-of-office replies too. See the 6.3.7 announcement. hMailServer filters mail with two systems that both run during delivery and do not know about each other. Rules are the server's own engine: criteria and actions, edited in the Control Panel, and the only one of the two that can run server-wide. Sieve (RFC 5228) is the standard mail filtering language, owned by the mailbox user and edited from a mail client over ManageSieve or on the Control Panel. This topic covers the order they run in, rules and the Run function action, Sieve and vacation, the out-of-office policy, and what both see on sealed mail. It describes 6.3.4 (27 September 2026, schema 6104). The one thing to know first: they run at different points, they see the message at different stages, and when they disagree about where a message goes, Sieve wins. The order they run in The filtering pipeline for one inbound message: During the SMTP conversation, while the sender is still connected. The pre-transmission spam tests (SPF, DNSBL, HELO, greylisting) run at MAIL FROM and RCPT TO; the script event OnSMTPData runs at the DATA command, before any of the body is sent; once the body is in come the post-transmission tests (SURBL, DKIM, ARC, SpamAssassin). A message over the delete threshold is refused, with 550 when the verdict came before the body and with 554 after it; with quarantine on, one judged after the body is held in quarantine instead and the sender answered 250. Otherwise the script event OnAcceptMessage runs and the message is queued. On the delivery thread, once per message and on every delivery attempt: OnDeliveryStart, blocked attachments and the virus scan, the global rules, OnDeliverMessage, then DKIM signing and mirroring (first attempt only). The message is split into local and external recipients. External recipients go to relays, routes and MX delivery. For each local recipient, on that recipient's own copy: per-account spam overrides, the account auto-reply, the account rules, account forwarding, trace headers, the Sieve script, the domain-wide auto-reply (only if the account said nothing), Sieve reject then discard, Sieve flags, the folder decision, and the save to the mailbox. Because It follows that Spam scoring happens during the SMTP conversation A rule cannot make a message spam; it can only read the flag, or a header the filter wrote Virus scanning happens before global rules A rule never sees an infected message Global rules run on the shared message, before the split Delete e-mail in a global rule destroys the message for every recipient; in an account rule, only that recipient's copy Global rules re-run on every delivery attempt Forward, Create copy and Reply can fire more than once for a deferred message. Write Delivery attempts equals 1 to prevent it Sieve runs after account rules A Sieve fileinto overrides the folder a rule chose. A header a rule set, or a copy it queued, has already happened and Sieve cannot undo it What a rule is Rules in the Control Panel sidebar is the server-wide set ("Global rules"); each account has its own on the Rules tab of its account dialog. A rule is a name, an enabled flag, a match mode - Match ALL criteria (AND) or Match ANY criterion (OR) - an ordered list of criteria and an ordered list of actions. Criterion What is compared Note From, To, CC The From:, To: and Cc: headers Not the envelope. Mail that reached you by Bcc, an alias or a list has no To: naming you Body The plain-text body concatenated with the HTML body A phrase inside an attachment is not matched Message size The file's size in bytes 25 MB is 26214400 Recipient list Every SMTP envelope recipient, joined with ; The field that sees a Bcc, an alias expansion or a list member Delivery attempts The retry count plus one The first attempt is 1 Custom header The named header's value Name it without the colon Match types. equals, does not equal, contains and does not contain are case-insensitive. is less than and is greater than parse both sides as numbers, and a non-numeric string parses as 0. matches wildcard and matches regex require the whole value to match; a regex is also case-sensitive, so to find a word anywhere write .*invoice.*, and (?i).*invoice.* to ignore case. does not equal and does not contain are true when the field is empty: Subject does not contain [ok] -> Delete e-mail deletes every message that has no Subject at all. Add a positive criterion under match mode ALL when the action is Delete. Action What it does Delete e-mail Global: the message is dropped and Message will be deleted. Action triggered by a global rule (<name>) is logged. Account: only this recipient's copy Forward e-mail Queues a second message to the named address, loop-count guarded Reply Queues a new message to the envelope sender, stamped Auto-Submitted; never answers auto-submitted mail Move to IMAP folder Happens at the end of local delivery. INBOX.Archive; #Public.Name for a public folder, subject to the insert permission; %YEAR%, %MONTH% and %DAY% come from the server's local date. A missing folder is created Run script function Calls a function in the event script - next section Stop rule processing Stops the rule set after this rule's remaining actions have run Set header value Rewrites the message file; %MACRO_ORIGINAL_HEADER% is the header's current value Create copy Queues a duplicate carrying X-CopyRule: <rule name> Send using route, Bind to address Force a named route, or the local address the connection leaves from. Absent from an account's rules The Run function action, since 6.3.4 The action the Control Panel calls Run script function calls the named function in the event script as <function>(HMAILSERVER_MESSAGE), then reloads the message from disk, so a script that edits and saves is seen by the rest of the rule set. It needs scripting enabled. Since 6.3.4 the name must be a plain identifier: ASCII letters, digits, _ and $, not starting with a digit. On Windows, in either script language (VBScript or JScript), any other name is refused as HM5018 each time the rule fires. Before 6.3.4 the name was written into the call unescaped, in either language, so whoever may edit a rule, a domain's own administrator included, could run code in the server's process: that is CVE-2026-100741, in every release from 6.0.0 to 6.3.3, and event scripts and the 6.3.4 security fix has the flaw and what to do until a server is upgraded. On Linux the action works since 6.3.4, where event scripts are JavaScript. The Linux engine looks a handler up by name instead of building a call, so such a name is never evaluated and finds no function: nothing is run, and the ERROR log has a Script Error line, not HM5018. Since 6.3.7 such a name is refused when the rule is saved, over COM and REST, for a rule of every owner; and the action on a domain's or an account's rule is the server administrator's to set, since it reaches a function of the server's event script: a credential confined to named domains - such as a domain administrator, or an API key issued for its domains - is refused it. Evaluation: a matching rule does not stop the set For each active rule, in order, the criteria are evaluated under the match mode and, if they matched, every action runs. Only a Stop rule processing action ends the set, so make that the last action of every rule that should be terminal. A rule with no criteria runs no actions, in either match mode. A catch-all rule needs a criterion that is always true. If the message file cannot be loaded, the whole set is skipped and the message is delivered unfiltered, with HM6040 in the log, rather than run against an empty message. When several choose a folder, a Sieve fileinto beats an account rule's folder, which beats a global rule's. The regular-expression breaker. A regex runs against a value the sender wrote, so it has a budget of 250 ms per evaluation. A pattern that will not compile is reported as HM6042; one that takes 250 ms or more as HM6043; either is suspended for 300 seconds, keyed on the pattern text, so a corrected pattern takes effect on the next message. A suspended pattern does not match. The same breaker covers Sieve's :regex, and suspension is shared. Loop protection. Every generated message carries X-hMailServer-LoopCount. Forward, Reply and Create copy check it against RuleLoopLimit (COM Settings.RuleLoopLimit; Mail flow & delivery → Delivery of e-mail → Rules, since 6.3.7 a card on the Rules page; default 5); at or above it nothing is generated and HM5064, HM5065, HM5201 or HM4404 is logged. An Auto-Submitted header stops Reply and bounce reports only; Forward deliberately ignores it. What else 6.3.4 changed for rules Three collections instead of two. A domain's rules (Domain.Rules, /api/v1/domains/{name}/rules) run after the server's. Account rules are on /api/v1/accounts/{address}/rules. Five more actions: Add a disclaimer, Reject with a text, Require TLS, Quarantine and Warn the sender. A legal hold keeps, among the account's deleted items, what an account rule's Delete or Reject, or a Sieve discard, reject or redirect that keeps no local copy, dropped. It does not reach the server's or a domain's rules: a Delete there drops the message for everybody, held accounts included. A domain's rules do not always act on the whole message. Mail flow rules and what a domain does to a message says when they act on all of it and when on the domain's own recipients only, what each new action does, and two 6.3.4 fixes to what a rule queues. Sieve: where a script lives and how it becomes active Each account has one active script, plus any number of named scripts. They are files, not database rows, under [Directories] DataFolder: <DataFolder>\ Sieve\ _global\ administrator-placed scripts, for include :global example.com\ alice\ active.sieve THE ACTIVE SCRIPT - this is what delivery runs vacation.responses who has already had a vacation reply, and until when duplicate.seen identifiers the duplicate test has seen scripts\ holiday.sieve named scripts, from ManageSieve PUTSCRIPT Four routes write that same active.sieve, and all four check the syntax first: Route Where Control Panel (Windows) Account dialog, Sieve tab COM (Windows) Account.SieveScript; no Save() is needed REST GET and PUT /api/v1/me/filters, as the account itself; 256 KB cap; an empty script removes the filter ManageSieve PUTSCRIPT then SETACTIVE Deactivating a script also clears that account's vacation-response records. ManageSieve is off by default: ManageSieveServerPort=0 means the listener is never created. Set the port (the wiki's example is 4190) and ManageSieveServerBindAddress on the ManageSieve (RFC 5804) card of the Control Panel's API & monitoring page on Windows (since 6.3.7 a ManageSieve tab of the Protocols page; Ctrl+K takes the old title to it), or on either platform with PUT /api/v1/settings/ini/{name} or hmctl ini set. Since 6.3.4 both wait for a reinitialise, not a restart: Apply now in the Control Panel or the Deck, or POST /api/v1/server/reinitialize. A script is at most 1 MB. With no certificate, STARTTLS is not advertised at all; with no certificate and no range policy, keep the listener on 127.0.0.1. What this server's Sieve accepts and refuses require accepts fileinto, envelope, copy, relational, subaddress, vacation, vacation-seconds, imap4flags, body, mailbox, regex, ihave, environment, date, index, spamtest, spamtestplus, duplicate, editheader, variables, reject, ereject, include and enotify, and the comparators comparator-i;ascii-casemap, comparator-i;octet and comparator-i;ascii-numeric. What matters in practice: fileinto :copy is refused at upload. This server stores exactly one local copy. Use :copy on redirect. redirect without :copy cancels the implicit keep, and the message leaves the mailbox entirely. imap4flags stores only the five system flags, \Seen \Answered \Flagged \Deleted \Draft. A keyword is accepted, logged, and stored nowhere. spamtest reads the server's internal flag, never a header a sender could forge: "0" when the message was not classified, "10" when it was. Any threshold from 1 to 10 behaves identically. duplicate keys on Message-ID over 7 days by default, and its store fails open, because a wrong "duplicate" destroys mail. reject and ereject send a non-delivery report with status 5.7.1 to whoever the envelope names. Against forged senders use discard. editheader refuses Received and Return-Path, and since 6.3.7 From, Sender, Resent-From, Resent-Sender, DKIM-Signature, Auto-Submitted and X-hMailServer-LoopCount, at upload and at delivery, where a script stored before the upgrade still runs without the edit; enotify accepts mailto: only, and since 6.3.7 a notify goes from the account's own address unless its :from is the account's own address or an active alias pointing at it; include nests at most three levels. The everyday script: require ["fileinto", "mailbox", "spamtest", "relational", "comparator-i;ascii-numeric"]; if spamtest :value "ge" :comparator "i;ascii-numeric" "5" { fileinto :create "INBOX.Junk"; stop; } if header :contains "List-Id" "announce.example.com" { fileinto :create "INBOX.Lists.announce"; stop; } Vacation, and the out-of-office policy require ["vacation"]; vacation :days 7 :subject "Out of the office until 22 September" :addresses ["[email protected]", "[email protected]"] "I am away until 22 September. For anything urgent please write to [email protected]."; :addresses is not decoration: a reply is sent only when one of the user's own addresses appears in the envelope or the headers, and that list is what "the user's own addresses" means. :days defaults to 7; vacation-seconds gives :seconds, and :seconds 0 means "reply every time". The window is per sender, in vacation.responses, and that store fails closed: if it cannot be read, no reply is sent. The account's Auto-reply tab and the domain's Out of office tab are the server's own reply, separate from Sieve. At most one reply answers any message. The account's own, when it is on and inside its dates, comes first; a domain's reply answers only for accounts that have none. If both an account-level auto-reply and a Sieve vacation are configured, the account-level one wins. Account Domain General text VacationSubject, VacationMessage VacationSubject, VacationMessage Text for colleagues VacationInternalSubject, VacationInternalMessage the same two Colleagues only VacationMessageInternalOnly leave the general text empty Strangers get the domain's text - VacationExternalOverride A sender is internal when the envelope sender resolves to an account hosted here or to an active alias. That is whatever the submitting client said, so an internal text must never carry anything that cannot be shown to a stranger. Each sender is answered once per absence; a window - VacationMessageDays on the account, VacationDays on the domain, 0 to 3650 - answers the same sender again after that many days. The memory is the table hm_autoreplies (schema 6060), so since 6.3.4 a restart no longer repeats every reply. Never answered: a null return path; Auto-Submitted other than no; Precedence: bulk, list or junk; anything with a List-* header; a sender that set X-Auto-Response-Suppress; postmaster and mailer-daemon; ourselves; and marked spam when VacationMessageAbortSpamFlagged says so; and since 6.3.7 a message whose X-hMailServer-LoopCount has reached RuleLoopLimit, so RuleLoopLimit 0 stops out-of-office replies as it stops every other generated message. Every reply goes out with a null return path and Auto-Submitted: auto-replied. What rules and Sieve see on sealed mail A message encrypted end to end (S/MIME or PGP/MIME) is opaque: the server holds no key to it. How the server decides that, and what the rest of the server does with such a message, is in end-to-end encryption and sealed mail. For rules and Sieve: A rule's body criterion on an opaque message does not match, whichever way it is phrased, and one line per rule per message in the log says so. Under AND, a rule with a body criterion does not fire; under OR, the other criteria are still evaluated. Before 6.3.4, does not contain matched every encrypted message. A multipart/signed message is read as it always was. To catch encrypted mail, test the Content-Type header for multipart/encrypted or application/pkcs7-mime with a custom-header criterion. Sieve's evaluation is unchanged; a log line is new. An encrypted message has no text part, so body :contains "x" is false and not body :contains "x" is true, and the delivery writes a log line saying so, once per message and recipient. body :raw is the undecoded body exactly as it arrived, so if body :raw :contains "BEGIN PGP MESSAGE" is the honest way a script can recognise a sealed message. What it does not do Sieve cannot run server-wide, force a route or bind an outbound address. A rule's Delete e-mail is silent. The sender was already answered 250. To tell the sender, use Sieve reject, or since 6.3.4 the Reject with a text action. A journal rule does not see a rule's forward, copy or reply, or a Sieve redirect: the server queues those itself. Where to read more Wiki: Rules and Sieve, The COM API and Scripting Documents at the 6.3.4 tag: OutOfOffice.md, SealedMail.md, MailFlowRules.md, EventScripts.md The public documentation: chapter 12 and the rules cookbook On this forum: the 6.3.4 release announcement, the Linux guide, mail flow rules and domain rules, event scripts, and when a saved setting takes effect for the ManageSieve keys
  • 0 Votes
    1 Posts
    124 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) regroups the Control Panel's sidebar: the Journaling and dual delivery page is Journaling & dual delivery, in the new Compliance group rather than under Monitoring & troubleshooting as the table below has it, and Ctrl+K takes the old title to it. See the 6.3.7 announcement. A journal rule sends a copy of a domain's mail, or of one account's, to an address. Exchange calls it journaling and sends an envelope report with the message attached; Google calls it dual delivery and sends the message itself. In hMailServer they are one mechanism with two shapes of copy, new in 6.3.4 (27 September 2026, schema 6104; the rules themselves arrived with schema 6075). This topic covers what a rule says, the two shapes, when a rule is evaluated, how it avoids looping, where it is edited, and what it deliberately does not do. The one thing to know first: a journal rule covers mail that comes in through the SMTP session, a fetch account or the send API, and nothing this server queues itself. A compliance archive built on a journal rule therefore has known gaps, and they are listed exactly below. Nothing is journalled until a rule exists. With an empty table the whole of this costs one comparison per accepted message. What a rule says Field Meaning scope domain or account: whose mail the rule is about target The domain name, or the account address. It must be a domain this server hosts, or an account in one direction inbound (a message with a local recipient the rule covers), outbound (a message from an authenticated sender it covers), or both journal_address Where the copy goes. One address, local or anywhere else, checked as an address before the row is written. It may be a mailbox in the very domain the rule journals; it may not be the account an account rule names mode report or copy enabled A rule that is off copies nothing and keeps everything it said note Why the rule exists. At most 255 characters The rules are stored in hm_journalrules. The row holds the domain id and, for an account rule, the account id, so nothing breaks when a domain or an account is renamed under it. A domain's rules go with the domain when it is deleted, and a rule naming an account goes with the account. A rule has no conditions. It covers all of a domain's or an account's mail in the direction it names. There is no "only messages over 10 MB" and no "only from these senders"; a mail flow rule is the place for that. The two shapes report is the Exchange journal shape: a multipart/mixed message whose first part is the envelope report and whose second part is the original message attached as message/rfc822, written through byte for byte. The report is four fixed fields, one per line, in one order: Sender: [email protected] Subject: Quarterly figures Message-Id: <[email protected]> Recipient: [email protected] Recipient: [email protected] The envelope is the point. A message's To and Cc headers are what the sender typed; the Recipient: lines are where the message actually went, blind copies and expanded lists included. A message with no envelope sender - a bounce - writes Sender: <>. copy is the message itself, as it was accepted, with one header line (X-hMailServer-Journal) in front of it and nothing else changed: not the body, not its transfer encoding, not its signature. The file is written byte for byte rather than through the MIME writer. What a migration or a second mail system wants is the mail, not a report about it; that is what copy is for. Both shapes are queued through the ordinary delivery queue, so a journal address somewhere else is delivered, retried and bounced as any other mail is, and a local one lands in a mailbox. The envelope sender of a journal message is the journalled domain's postmaster, not the person who wrote the message. A journal address that stops answering is the administrator's problem, and bouncing it to the sender would tell somebody outside this server that their mail is being copied. When a rule is evaluated Once per accepted message, in the SMTP session, after the row is saved and the message file is settled - the same place, and the same recipient list, the on-disk archive decision is made from. Not at delivery: the deliverer runs again for every retry and for every route a message splits into, and a compliance archive that gained a second copy because a remote host was busy would be worse than useless. One report or copy per matching rule per message, not per recipient. A domain rule that matches a message addressed to three accounts of the domain sends one report, with all three on it. There are three points of entry where rules are evaluated: The SMTP session. The outbound half judges an authenticated sender; this is what a desktop or phone client uses. A fetch account. Mail a mailbox collects from another provider by POP3 or IMAP is journalled once per message, at the same point in that path. A download has no authenticated sender, so it is the inbound half of a rule that speaks for it, and it has no SMTP session, so its lines go to the application log. The send API, POST /api/v1/send, which is new in 6.3.4 and is journalled in it (CHANGELOG lists the fix in its 6.3.4 section). The key's sender counts as the sender an outbound rule judges when that sender is an account here. What a journal rule does not see A message the server queues itself is queued past all three points and is not journalled in either direction, even when it is addressed to a mailbox a rule covers: Not journalled Note A message the webmail queues directly POST /api/v1/me/messages A send-later draft A message written by an event script A mail flow rule's forward, copy or reply An automatic reply A Sieve redirect An account's forwarding A delivery report Two consequences follow, and both matter to anyone relying on this for compliance: The outbound half covers SMTP submission and the send API, and not the browser. Mail a person sends from the webmail is not journalled by an outbound rule. The inbound half covers mail from outside this server, and not what the server sends on to a mailbox of its own. Two further limits of the same kind: A message that was refused is not journalled. A message rejected at RCPT TO or by a rule never existed as far as this is concerned. A message held for review is journalled, because it was accepted. Not looping There is one guard, and it takes nobody's word for anything. Every journal message this server writes carries X-hMailServer-Journal, and its value is the rule's id, a full stop, and a mark: 128 random bits the server process chose when it first needed one. The evaluation reads that header before it does anything else and stops only when the mark is this process's own. A journal message that comes back into this server - relayed by the far end, forwarded by the journal mailbox's owner - is never journalled again, and the guard stops the copy, not the delivery: the mailbox still gets its mail. A message carrying the header with any other value is journalled exactly as it would have been without it, and the attempt is noted in the log. The header's name is in the published document, so a guard that honoured the header as written would let anybody under an outbound compliance rule keep a message out of the archive by typing one line into their mail client. An archive that the people it is about can opt out of is not an archive. Three properties of the mark to understand: It is per process, and nothing is stored for it. A restart forgets the marks it wrote, so one of this server's own reports that returns after a restart is journalled once more, and the report that makes carries a mark the running process does know, so it stops there. A loop cannot run away; the price of keeping no secret on disk is one duplicate. It is not a signature over the message. It travels in the messages it marks, so whoever can read this server's journal mail - the journal address itself, and anything relaying to it - could quote a mark back in a message of their own and keep that one message out of the archive. If that relay is a worry, journal to a local mailbox, or over an authenticated route. There is no second guard, and no rule is skipped for where its journal address points. Journalling a domain into a mailbox of that domain works. example.com to [email protected] sends one report per message into that mailbox and nothing cascades, because a journal message is written straight into the delivery queue and never passes an evaluation point. It used to be refused at run time, silently: the rule matched, the copy was dropped with a line in the log, and every surface went on showing the rule as enabled. That guard went. The one arrangement refused is an account journalled into itself, and it is refused when the rule is written rather than found out later: it would put every message in the mailbox twice and archive nothing anywhere. Where it is edited Journal rules are the server administrator's. A rule says that somebody's mail is copied to an address outside their sight, so a credential confined to named domains is refused even though a rule names one. Surface How Platform REST GET/POST /api/v1/journal-rules, PUT/DELETE /api/v1/journal-rules/{id} Windows and Linux The Control Deck The Journaling view: every rule, a switch on each row, and a form to add one Windows and Linux The Control Panel The Journaling and dual delivery page, under Monitoring & troubleshooting (since 6.3.7 Journaling & dual delivery, in the Compliance group; Ctrl+K takes the old title to it), over three Utilities methods (GetJournalRules, SaveJournalRule, DeleteJournalRule) Windows only hmctl hmctl journalrule list\|create\|update\|delete A client over the REST API PowerShell module Get-HmJournalRule, New-HmJournalRule, Set-HmJournalRule, Remove-HmJournalRule Over the REST API Over REST, reading needs view_configuration and writing needs manage_server, so the auditor role reads the rules and writes none. Every surface goes through one function on the server, Journaling::SaveRule, so the three small vocabularies (domain/account, inbound/outbound/both, report/copy) mean the same thing whichever you use, and a refusal reads the same sentence. Writing, changing or removing a rule is on the audit trail. Setting up a compliance copy for one domain: Decide the journal address. A mailbox in the same domain, such as [email protected], is allowed; an address elsewhere is delivered through the ordinary queue. Create one rule with scope domain, target the domain name, direction both, mode report and the journal_address. Over SMTP - from a mail client signed in to an account, or from outside, and not from the webmail - send one message to two recipients of the domain, one of them as a blind copy, and open what arrives at the journal address: one report for the message, with a Recipient: line for each envelope recipient. Then send one message from the webmail and confirm that it is not journalled, so that whoever owns the archive knows the gap is there. A rule is refused before it is written for an unhosted target, a word outside the three vocabularies, no journal address, one that is not an address, one carrying a line break, and an account journalled into itself. Journaling beside three other features Sealed mail. The journal keeps the bytes verbatim, so a message that was encrypted end to end still decrypts from the copy - but nobody reviewing the journal can read its content. Restricted delivery. A journal rule's copy of a restricted account's mail goes where the rule says, because the copy is the administrator's and not the account's forward. Rules and Sieve. A rule's forward, copy or reply and a Sieve redirect are among the messages the server queues itself, so they are not journalled. What it does not do It is not the archive. ArchiveDir and ArchiveDomains keep files on this server; a journal rule sends mail elsewhere. Neither replaces the other, and turning one on does not turn the other off. It is not eDiscovery. A case searches the mailboxes, their deleted items and the archive on this server, and a journal copy has left it. A journal address that is a mailbox here is searched like any other custodian. It is not an account's forwarding. A forward is the account holder's, and can deliver instead of the mailbox. A journal copy is the administrator's, and never changes what the mailbox gets. It is not a mail flow rule's forward or copy action. Those have conditions, run per delivery attempt, and can be edited by the account. It is not retention. Once a journal message leaves the queue it belongs to whatever receives it. This server keeps no second copy, expires nothing and withdraws nothing; deleting a rule stops the next copy and does not reach the ones already sent. It is not encryption or redaction. A journal address outside your control sees everything the recipient sees. Where to read more The document at the 6.3.4 tag: Journaling.md, with Discovery.md, SendApi.md, SealedMail.md and RestrictedDelivery.md Wiki: Routes and Relays (section 14.2d) and Compliance and eDiscovery On this forum: the 6.3.4 release announcement; Rules and Sieve for the forwards and redirects a journal rule does not see; restricted delivery; the send API; compliance, eDiscovery and legal holds; and the archive
  • 0 Votes
    1 Posts
    127 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) signs an authenticated submission as another hosted domain only when the account may send as the address its From names - its own, an alias pointing at it, a mailbox that granted it the post right, an address it proved, or a masked address of its own; otherwise the mail is sent unsigned as that domain, so give such an account the alias or the grant before upgrading. Mail from outside whose From names a hosted domain is no longer signed as it, nor is an alias's or a distribution list's copy of it to an outside address; where such a message did not pass DMARC for the organisation its From names when it arrived, a copy whose envelope sender is at this server's host name, or at a hosted domain or a subdomain of one, in that organisation is not sent where an alias or a distribution list expands to an outside address, a managed list does not distribute a posting whose From names the list domain's organisation, and a distribution list's bounce address is not given to it; a managed list's copies of a posting from outside are not signed as the list's domain when an address of the list domain's organisation is in its From fields. Creating a domain alias is now the server administrator's: a credential confined to named domains is refused it, over REST and over COM. See the 6.3.7 announcement. There are three ways of making one address reach somewhere other than a mailbox of the same name - an alias, a distribution list and a route - and one way of giving several people the same folder. This topic covers all of them as they are in 6.3.4 (27 September 2026, schema 6104): aliases, domain aliases and plus addressing; distribution lists, who may post to them and how moderation works; the membership query, which lets a list define its own members; the list manager, which turns a distribution list into a mailing list people join and leave themselves; and public folders with their permissions. The membership query and the list manager are new in 6.3.4; the distribution list itself is in every release. The one thing to know first: the address is resolved in one function (RecipientParser::CheckDeliveryPossibility), in a fixed order, and an account always wins. The order an address is resolved in At RCPT TO the server works through this, and stops at the first answer: Domain aliases rewrite the domain. Plus addressing trims the local part. Account? If found, deliver to the mailbox. Alias? If found, follow it and start over, up to 25 hops. Distribution list? If found, expand to the members, or to the moderator. Route? If found, send to the route's target server. The domain's postmaster address, the catch-all: when nothing above matched, the resolution starts again with that address, which is why a mistyped address is delivered there rather than refused. If it is empty, 550 Unknown user. Since 6.3.4 one question is asked before all of these: is the address, or its domain, routed to a webhook? A webhook route that only posts takes the address's mail instead of its mailbox, alias or list - a list whose address is routed never sees the message, nor applies its posting rules to it. Route the addresses you mean rather than a list's whole domain; mail in to a webhook has the routes themselves. Aliases An alias is a second address that delivers to an existing target. In the Control Panel: Accounts & domains → Domains, select the domain, then the Aliases panel. The Control Panel and COM are Windows-only. On Linux, where there is neither, the Control Deck is the administration people click through, and it is the front end of the REST API, which both platforms have: What REST route A domain's aliases GET and POST /api/v1/domains/{domain}/aliases; DELETE /api/v1/aliases/{address} A domain's other names GET and POST /api/v1/domains/{domain}/domain-aliases; DELETE on .../{name} beneath it A domain's lists GET and POST /api/v1/domains/{domain}/lists; PUT and DELETE /api/v1/lists/{address} A domain's plus addressing PUT /api/v1/domains/{domain} (plus_addressing_enabled, plus_addressing_character) The Control Panel paths and COM properties below are for Windows. Field COM property Meaning Alias address Alias.Name The address people send to. Must be in this domain Target Alias.Value May be anything: another account here, another alias, a distribution list, an address covered by a route, or an address at a different server Active Alias.Active Unticked: RCPT TO is answered 550 Alias is not active. - not passed on to the catch-all The target replaces the address and resolution starts again, so an alias may point at an alias, which may point at a list. Beyond 25 hops the answer is 550 Mail server configuration error. Too many recursive forwards. An alias is only consulted when no account has that address. Creating sales@ as an account after sales@ exists as an alias silently changes where the mail goes. An alias to an external address is treated as inbound mail, not as relaying, so [email protected] → [email protected] works for external senders without opening a relay. Since 6.3.4 three more kinds of second address exist, and they are the person's own rather than the administrator's: the alias a mailbox move leaves (renaming or moving a mailbox keeps the old address as an alias of the new one by default); masked addresses (schema 6067), which the person makes in the webmail for one shop or sign-up and which, switched off, are refused as an address that never existed; and verified send-as (schema 6066), which receives nothing here - it is a From address, not an alias. Domain aliases. Accounts & domains → Domains → Properties → Names. The rewrite is total: [email protected] becomes [email protected] before anything else is looked at. You do not create per-address aliases for an alias domain; the sender address is rewritten too, which is what lets a list in membership mode recognise a member who posts from the alias domain; and the alias domain needs its own MX record. Since 6.3.7 adding one is the server administrator's: a credential confined to named domains - such as a domain administrator, or an API key issued for its domains - is refused, over REST and over COM, because a domain alias decides what names the server accepts mail for. Plus addressing is off by default and enabled per domain on Accounts & domains → Domains → Properties → Limits (default character +). With it on, [email protected] resolves to [email protected], and the full original address is still in the To: header. It applies to the recipient address only, after domain aliases and before the account, alias and list lookups. When off, alice+invoices@ is an unknown address. Distribution lists A distribution list delivers one message to many recipients. In the Control Panel: the Distribution lists panel of the domain, then Properties and Members. Mode Stored value Who may post Refusal Public - anyone can send 0 Anyone at all - Membership - only list members can send 1 Any address on the member list, after domain aliases are applied to both sides 550 Not authorized sender. Announcements only 2 Only the address in Require sender address 550 Not authorized owner. Anyone in the domain can send 3 Any sender whose domain is the list's own 550 Not authorized domain. (on any mode) Require SMTP authentication flag An unauthenticated session is refused first 550 SMTP authentication required. "Anyone in the domain" is a claim, not a proof. MAIL FROM is free text, so an outsider can assert a local address. Tick Require SMTP authentication with that mode or the list is open to the Internet. A fifth mode, 4, "Anyone with a server account can send", existed only in the type library and behaved as Public; put_Mode now refuses the value and the option is gone from the dialog. One more check runs for every mode, Public included: the poster must be allowed to reach every recipient of the list, so a list containing a members-only list the poster is not in is refused as a whole. Moderation. Set a Moderator address and a posting the rules above would refuse is accepted with 250 and forwarded to the moderator instead, stamped X-hMailServer-Moderation. The moderator approves it by resending it to the list from an authenticated session; that authentication is the security boundary and is unconditional. Three failures are numbered in the log: 6360 (the moderator was removed between the check and the expansion), 6361 (the moderator address does not resolve; the recipient is refused with 550) and 6363 (the list is its own moderator). Bounce address. Empty means bounces go to the poster. Set, it becomes the envelope sender of every copy, so a dead subscriber's bounce reaches the list owner, and an external member's server evaluates SPF against the list's own domain. A bounce address pointing at the list itself is ignored, with error 6362. Headers. An unmanaged list adds List-Id, List-Post (the literal NO for an announcements-only list), List-Help, List-Owner and a mailto:-only List-Unsubscribe, before DKIM signing. Nothing is added when the message already carries a List-Id, or when the list or owner address is not US-ASCII. A list that defines itself: the membership query, since 6.3.4 Schema 6074. A list can carry a membership query: one short text that says who its members are, answered against the server's accounts at the moment a message is sent to it. [email protected] with the query domain reaches every active account of the domain on the day, including the person who joined this morning and not the one switched off last night. Query Members (empty) A static list: the addresses under Recipients and nothing else domain Every active account of the list's own domain domain:other.example Every active account of that domain, which this server has to host pattern:sales-*@example.com Every active account whose address matches the wildcard; * is any run, ? exactly one, case ignored everyone Every active account of every active domain on the server It is a union with the list's own recipients, never a replacement. An address that is both is delivered to once. It answers accounts only - not aliases, other lists or outside addresses - and never a disabled account or an account of a disabled domain. It is resolved once per SMTP transaction, not once per RCPT TO, and nothing is stored. Who may set which form. A domain administrator, or an API key with a domain scope, may set domain or a pattern: in a domain it holds. everyone, domain: naming another tenant's domain, and a pattern whose domain part is itself a wildcard are the server administrator's. The same rule governs reading the answer: GET /api/v1/lists/{address}/members answers 403 to a confined credential when the query reaches past its domains. A query is judged when it is saved. domain: naming a domain this server does not host, a pattern: with no @, or any text that is none of the five forms is refused, by the same sentence over COM, over REST and in the Control Panel. "Answers nobody" and "could not be answered" are different. A query whose named domain has been deleted is not an error at delivery: the message goes to the list's rows and code 6980 is logged. But when the accounts cannot be read - the database locked by a backup - the recipient is refused 451 4.3.2 and the sender retries, with code 6982, and the members route answers 503. It is set as Membership query in the Control Panel's list dialog and the Control Deck, membership over REST, DistributionList.Membership over COM, and hmctl list update [email protected] --set membership=domain. The Deck's list editor shows Delivers to now, each address marked Kept by hand or From the query. The list manager: a mailing list, since 6.3.4 Schema 6092. A distribution list with the list manager switched on is one people join and leave themselves. Nothing changes for a list until it is switched on: every list an upgrade finds is unmanaged. On the list: Managed in the Control Panel's list dialog, managed in the Deck's list editor and on PUT /api/v1/lists/{address}, or DistributionList.Managed over COM. The links. The confirmation page, each member's page, the owner's approval page, the subscribe form and the archive are served by the REST listener, which is off until RestApiPort is set (turning the REST API on). Make it reachable over HTTPS, then set list_base_url (ListBaseUrl), such as https://lists.example.com. Empty, the default, offers no link at all. Only an https:// base gives RFC 8058's one-click. DKIM on the list's domain. The copies are signed as that domain after the list's fields are in place, so the signature covers List-Unsubscribe and List-Unsubscribe-Post. With DKIM off for the domain the copies go unsigned and the providers do not offer the one-click. Address What it does [email protected] Post to the list [email protected] Ask to join, and confirm by replying [email protected] Ask to leave: the member is sent a link and a token, and nothing more happens until they follow it [email protected] The help text [email protected] The person who runs the list [email protected] Delivery reports; with VERP, [email protected] None of them is an account; an account, an alias or a list of exactly one of those names is the administrator's and wins over the manager. Joining. subscribe_policy is 0 closed (the default: the administrator adds members), 1 confirm (the address asked for is sent a confirmation with a token) or 2 moderated (after the address confirms, the owner approves on a page, signed in to the webmail as the owner's own account, or on the Deck; a reply by mail approves nothing). The list answers at most twenty requests an hour from one requester and two hundred an hour in all. Delivery. Each member chooses each message, one digest a day, or none. With digest on, one digest goes at digest_hour (7 by default, the server's local time), at most 100 messages and 10 MB. With archive at 1 (members) or 2 (public), every distributed message is kept and shown by month, as text only, never as HTML. The rows of hm_listarchive are not in the backup. What a member's copy carries, over an https:// base with the archive on: List-Id: <team.example.com> List-Post: <mailto:[email protected]> (NO for an announcement list) List-Help: <mailto:[email protected]?subject=help> List-Subscribe: <mailto:[email protected]?subject=subscribe>, <https://lists.example.com/lists/[email protected]/subscribe> List-Unsubscribe: <mailto:[email protected]?subject=unsubscribe>, <https://lists.example.com/lists/unsubscribe?t=...> List-Unsubscribe-Post: List-Unsubscribe=One-Click List-Owner: <mailto:[email protected]> List-Archive: <https://lists.example.com/lists/[email protected]/archive> Precedence: list X-Loop: [email protected] X-hMailServer-List-Member: <a tag, see Bounces> Bounces. A 5.x.x is a hard bounce, a 4.x.x a soft one; a delayed warning counts for nothing. Anybody can write to the bounce address, so a report is believed only when it carries the member's tag, X-hMailServer-List-Member. Setting REST key Default ListHardBouncesToUnsubscribe list_hard_bounces_to_unsubscribe 1 ListSoftBouncesToUnsubscribe list_soft_bounces_to_unsubscribe 5 ListBounceWindowDays list_bounce_window_days 30 A member who reaches either count is set to none - paused, never unsubscribed - and the owner is told once. Public folders Accounts & domains → Public folders, in the Control Panel on Windows, creates shared IMAP folders; public folders have no REST route yet, so Linux has no administrator's page for them. On either platform a client holding the a right manages a folder's permissions over IMAP. Public folders live in their own namespace, #Public, and sub-folders are made by typing the whole path with the hierarchy delimiter, . by default: #Public.Team.Archive. #[email protected] is a different namespace: one account's own folders, shared. Each permission entry grants RFC 4314 rights to an account, a group or Anyone (every authenticated account). Right Letter What it allows Lookup l The folder appears in LIST/LSUB. Without it the folder does not exist as far as that user is concerned Read messages r SELECT and STATUS Keep seen/unseen state s Set or clear \Seen Set flags w Flags other than \Seen and \Deleted Insert i APPEND and COPY into the folder Post p Send mail to the folder's submission address Create sub-folders k CREATE beneath it Delete folder x DELETE Delete messages t Set or clear \Deleted Expunge e EXPUNGE Administer a SETACL, DELETEACL, GETACL, LISTRIGHTS An owner has every right implicitly. For anyone else the server walks up from the folder to the nearest ancestor that has any ACL rows, then uses the row for the account, else for a group the account is in, else for Anyone. The nearest ancestor with any rows wins, and rows are not merged. Adding one permission to a sub-folder removes everyone else's access to it. \Seen is shared in a shared folder, not per user. Grant lrs and delegates share one read/unread state; grant lr and they leave no trace. Turning ACL enforcement off opens public folders only. It does not extend to #Users, which stops existing entirely. What it does not do The list manager does not rewrite From for DMARC. A posting from a domain that publishes p=reject reaches outside members with a From that fails DMARC when the list changed the message, and Gmail and Yahoo may refuse it. No posting from the web, no list password, no approval by reply, no NNTP, no IMAP access to an archive, no weekly digest, and one owner address per list. A membership query has no directory-attribute form and no group: form, and no query can name another list's query. The list manager's fixture is not linked into the Linux test suite: MailingLists.md says its thirty-four tests are of the Windows suite. Where to read more Wiki: Aliases, Lists and Public Folders, Mailing Lists, Mail In to a Webhook Documents at the 6.3.4 tag: DistributionLists.md, MailingLists.md The public documentation: chapter 13 On this forum: Being trusted by the servers you send to for DKIM and DMARC, the 6.3.4 release announcement, the webmail in depth for masked addresses and send-as, and IMAP on the server for shared mailboxes
  • 0 Votes
    1 Posts
    128 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) holds a credential confined to named domains - such as a domain administrator, or an API key issued for its domains - to a public address when it changes a domain's relay host, so it cannot point the server's delivery at the server's own network, and makes a domain's or an account's rule's Send using route and Bind to address actions the server administrator's to set; an account given the serveradmin role with a list of domains is, over COM as over REST, confined to those domains, so a script's account that edits the routes or the relayer needs the role with no list. See the 6.3.7 announcement. There are four ways this server can decide where an outbound message goes, and they are tried in a fixed order. Almost every "it went to the wrong server" question is answered by knowing that order, so this topic starts with it. It then covers the smart host and its failover, per-domain relays, routes and what they do to an unknown recipient, sending an OAuth2 token to a relay and what 6.3.4 changed about it, the COM change that is in effect only after Save(), and three things new in 6.3.4 (27 September 2026, schema 6104) that are not about where mail goes: remote domain policies (one paragraph here; the fuller treatment is Requiring TLS: MTA-STS, DANE and remote domain policies) and restricted delivery, which decide whether mail may go, and message recall, which withdraws unread copies already delivered on this server. The one thing to know first: a route is a statement about the destination and beats everything; a per-domain relay is a statement about the sender; the server-wide relayer is the fallback for mail nothing else has an opinion about. The order of decision When a message is ready to leave, its recipients are grouped by domain and the server asks: Did a global rule apply the Send using route action? Then every recipient goes to that route's target host and nothing else is consulted. Is there a route whose domain matches the recipient domain? Wildcards are allowed. The route's host, port, connection security and credentials are used. Does the sender's local domain have a relay host of its own? That domain's relay host, port, security and credentials. Port 0 means 25. Is a server-wide SMTP relayer configured? The relayer's host, port, security and credentials. Port 0 means 25. Otherwise, an ordinary MX lookup for the recipient domain, then A or AAAA for each MX host. Targets that turn out to be identical are merged: a message for four domains that all relay through one smart host opens one connection, not four. A bounce never picks up a per-domain relay, because a bounce has no envelope sender, so there is no sending domain to consult. Since 6.3.4 one question comes before all of these for inbound mail: an address or a hosted domain routed to a webhook has each message posted to an application, instead of being delivered (deliver off) or as well (deliver on). That is a different thing from the routes below, which send a domain's mail to another mail server; mail in to a webhook has the webhook routes. Sending through a smart host On Windows, configure it in the Control Panel at Mail flow & delivery → Delivery of e-mail → Relayer. The Control Panel and the COM API are Windows-only; over REST, which Linux has too, the relayer's settings are keys of PUT /api/v1/settings (smtp_relayer_password is write-only). Setting COM name Default When it is wrong SMTP relayer SMTPRelayer empty: MX lookup A host that does not resolve gives the bounce text "The host specified as SMTP relay server could not be found" with 4.3.5, a temporary failure, so the message is retried Port SMTPRelayerPort 25 Looks like a connection timeout in the SMTP log Connection security SMTPRelayerConnectionSecurity None Required against a host without STARTTLS defers every message Relayer requires authentication SMTPRelayerRequiresAuthentication off Off against a host that requires it gives 530/535 and the mail stays queued Number of delivery retries SMTPNoOfTries 4 0 means one attempt and then a bounce Minutes between retries SMTPMinutesBetweenTry 60 Outbound delivery security SMTPConnectionSecurity STARTTLS (optional) Applies to MX-resolved delivery, not to the relayer Several smart hosts. Separate the names with a vertical bar: smtp1.example.net|smtp2.example.net Each name is resolved in turn and every address it resolves to is appended, giving one ordered candidate list tried top to bottom. A single name with several A records already gives that failover. All hosts in the list share the port, security and credentials. A permanent 5xx rejection is a real delivery failure and the next host is not tried: trying it would deliver the message twice if the first server had in fact accepted it. Relaying everything through an ISP: Open Mail flow & delivery → Delivery of e-mail → Relayer. SMTP relayer: smtp.isp.example. Port: 587. Connection security: STARTTLS (required) - required, not optional, so a downgrade cannot send your credentials in clear. Tick Relayer requires authentication and fill in the user name and password. Save changes, send one message, and read Monitoring & troubleshooting → Live logs. The line that proves the relayer was used is: "SMTPDeliverer - Message 41: Relaying to host smtp.isp.example." If the message went to an MX host instead, the relayer field is empty, or a route or a per-domain relay took precedence. Per-domain relay. Accounts & domains → Domains → Properties → Relay sets where mail from that domain leaves through. It is for a server hosting several independent domains where each has bought its own delivery provider. An empty host means the domain has no opinion. Since 6.3.7 a credential confined to named domains - such as a domain administrator, or an API key issued for its domains - may set the host to a public address only, over REST and over COM; a host on the server's own network is the server administrator's to set, and one the server administrator set stays the domain administrator's to save, since only a change of the host is judged. Routes A route overrides delivery for a specific recipient domain. Mail flow & delivery → Routes: type the domain and target host, press Add, then Properties for everything else. Over REST the routes are /api/v1/routes. Field Default Note Domain name - Wildcards are matched, so *.partner.example works. The first matching route in name order wins; a wildcard broader than you meant silently captures other domains Target SMTP host, port -, 25 A name or a literal IP address; the host1\|host2 failover applies here too Number of delivery retries 3 Replaces SMTPNoOfTries. The delivery path applies it as tries − 1, so a route set to 3 schedules 2 further attempts Minutes between retries 10 Where one message has recipients on several routes, the largest count and the largest interval win Deliver to all addresses on Off: only the addresses listed are routed. Off with an incomplete list is the single most common route mistake Connection security None None, SSL/TLS, STARTTLS optional, STARTTLS required Treat sender domain as local off Turn it on only for a domain you actually control Treat recipient domain as local off On, unauthenticated senders may send to it from the Internet range What a route does to an unknown recipient: Route setting Domain has a catch-all? Address in the route's list? Result Deliver to all addresses - - Routed Specific addresses only no no 550 Recipient not in route list. Specific addresses only yes no Delivered to the catch-all, not routed Specific addresses only yes yes Routed; the catch-all does not intercept it Routes also make ETRN work: when a remote site issues ETRN <domain> and a route matches, the messages held for it are released for immediate delivery. OAuth2 to a relay A route or the SMTP relay whose host is on OutboundOAuth2Hosts (default smtp.office365.com) is sent AUTH XOAUTH2 with the server's own token: a client-credentials token fetched from OutboundOAuth2TokenUrl with OutboundOAuth2ClientId, OutboundOAuth2ClientSecret and OutboundOAuth2Scope (default https://outlook.office365.com/.default), or OutboundOAuth2FixedToken used verbatim instead of fetching. The default host list is inert on its own: with OutboundOAuth2TokenUrl and OutboundOAuth2FixedToken both empty, no bearer is set and the delivery proceeds with LOGIN and the password, with no log line. Since 6.3.4 the token is sent over TLS only. A route or the SMTP relay whose host is on OutboundOAuth2Hosts and whose connection security is none - or STARTTLS optional against a server that does not offer it - used to send AUTH XOAUTH2 with the token in the clear. Such a delivery now sends nothing in its place, fails with a temporary 450 4.7.0, and the error log says to set the security to TLS or STARTTLS required (HM5912); the message waits for the retries the route allows. Exchange Online offers STARTTLS on 587, so a route set up as Microsoft documents it is not affected. Also since 6.3.4, a relay that refuses the login no longer gets the token into the bounce, which now reads hMailServer sent: AUTH XOAUTH2 <Token removed>; the logs write AUTH XOAUTH2 ***. These are [Settings] keys, and since 6.3.4 the database is the settings store: a line edited in the file changes nothing, the stored value is used and named as HM5804, and the line is put back where the service can write the file (when a saved setting takes effect). Change them in the Control Panel, with PUT /api/v1/settings/ini/{name} or with hmctl ini set. A COM change is in effect only after Save() Since 6.3.4 a COM change to a route or an incoming relay is in effect only after the object's Save(), and a property set and never saved changes nothing; the list of objects this applies to does not name the server-wide SMTP relayer's settings. COM is Windows-only, so this concerns scripts on Windows: the COM API topic has the whole list, what an object read before the save shows, and error 7390. Remote domain policies, in one paragraph Since 6.3.4 (schema 6045). A route says where mail for a domain goes; a remote domain policy says what this server will and will not do when it talks to one domain it does not host, however that domain's server was found, and it applies to a routed delivery exactly as to an MX one. One record per name or pattern (bank.example, *.example, *), the most specific active one governing, holds the outbound TLS the domain demands (none, encrypted, verified or dane; a delivery that cannot meet it is deferred with 4.7.0, never sent in the clear), TLS required of its senders (530 5.7.0), a largest message (5.3.4), connection and rate ceilings (deferred with 4.4.5), whether automatic replies and forwards may go there, a recipient callout for a domain this server is a backup MX for, and gateway encryption to the partner's own key. It is server-administrator only, and GET /api/v1/remote-domains/effective?domain=mail.bank.example answers which record a delivery will use. Restricted delivery, since 6.3.4 Schema 6084. Accounts, or a whole domain, that may only exchange mail with named domains: a school whose pupils write to the school and to nobody else. It is not a route: it decides whether mail may go, not where. The list is the domain's: Domain.RestrictedDomains over COM, restricted_domains on GET/PUT /api/v1/domains/{domain}, the Restricted delivery tab of the Control Panel's domain dialog, the same group in the Deck's domain editor. At most 2,000 characters, names separated by commas or spaces, ASCII only, an internationalised name in its xn--... form. Written as Covers partner.example that domain, and nothing beneath it .example example and everything beneath it, anchored at a label boundary An account follows its domain unless it says otherwise (Account.RestrictedDelivery, restricted_delivery on the account routes): Value Word Meaning 0 domain Follows the domain. The default 1 restricted Restricted even when the domain names nothing; the account is then confined to its own domain 2 exempt May exchange mail with anyone An empty list, the default, means nothing runs, so an upgraded server delivers exactly as it did. Always allowed: the account's own domain under its own name or any alias domain, the domain's postmaster, the null sender MAIL FROM:<>, and mail this server writes itself. Other domains this server hosts are not allowed unless named. It is enforced in three places: Inbound, at RCPT TO. A restricted recipient whose envelope sender is not allowed is answered 550 5.7.1 <address>: this mailbox accepts mail from named domains only., per recipient, so one refused RCPT does not end the transaction. Every session is judged, authenticated or not. Outbound, at RCPT TO. A restricted submitting account is answered 550 5.7.1 <address>: this account may send to named domains only. for a recipient outside. The webmail's POST /api/v1/me/messages answers 400 with the sentence and queues nothing. At local delivery, for a recipient reached by expansion: a distribution list member, an alias target, a catch-all. The restricted recipient is dropped, and a non-delivery report goes to the sender only when the sender is local. A restricted account's forward - the account forward, an account rule's forward, a Sieve redirect - to a domain not allowed is not made, and the message is still delivered to the mailbox. Its out-of-office reply is not sent to a sender who is not allowed. Every refusal is a DEBUG log line; nothing goes to the error log, since a refusal is the feature working. Message recall within the server, since 6.3.4 Schema 6085. An unread message sent to accounts on this server can be withdrawn by its sender. Nothing does this across the Internet: a message that has left for another server is that server's. No recall notice reaches the recipients; the Sent copy's own mark is the record. What is recorded. A message accepted from one of the server's own accounts - an authenticated SMTP submission, credited to the account that signed in, or the webmail's send - gets one row in hm_recall per recipient that resolved to a local account. Nothing is recorded for: mail from outside, and mail this server writes itself; an unauthenticated submission from a local IP range in a local account's name, because a recall keyed on a claim would let anybody delete mail from other people's mailboxes; a message with a Message-ID longer than 255 characters, and a message that expires; the 201st and later recipient rows of one sender in a rolling day. The recall. In the webmail, Recall in the menu of a message in the Sent folder. For a program, POST /api/v1/me/recall with {"message_id": "<the Message-ID>", "sent_id": n}; it is refused with 403 on an app password. Each recipient is judged on their own: Outcome Meaning read Some live copy of theirs carries \Seen. Nothing was done: whoever has read it keeps it recalled No copy was read; every unread copy has been deleted the way the webmail deletes a message gone No copy of theirs is there any more: deleted by the reader, never delivered, or still in the queue failed The database did not answer; a later call decides them again The answer also counts elsewhere, the To and Cc addresses whose domain this server does not host, and says in sent_marked whether the Sent copy carries the keyword $Recalled. Read is \Seen and nothing more: a POP3 client, or an IMAP client that peeks, has the message and the recall cannot know. RecallDays (recall_days on PUT /api/v1/settings) is 14 by default, 1 to 365; a domain or an account may hold a shorter window, never a longer one. There is no COM property and no Control Panel page. What it does not do A route does not decide whether mail may go, and restricted delivery and a remote domain policy do not decide where. A per-policy SMTP port or credential is a route's job. Restricted delivery does not check the From header, does not filter what is already in the mailbox, and does not gate a journal rule's copy or an alias or list in the domain that reaches outside. Recall does not reach another server, does not touch the queue, and does not recall a message submitted without authentication. A 5xx from one smart host is not retried on the next. Where to read more Wiki: Routes and Relays, Settings Reference for the OutboundOAuth2* keys, APIs Reference, Diagnosing Stalled Mail Documents at the 6.3.4 tag: RemoteDomains.md, RestrictedDelivery.md, MessageRecall.md, Upgrading.md The public documentation: chapter 14, Routes and relays On this forum: Being trusted by the servers you send to, the 6.3.4 release announcement, requiring TLS and remote domain policies, and diagnosing stalled mail for a message that will not leave the queue