Journaling and dual delivery: a compliance copy, and what a journal rule does not see
-
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 scopedomainoraccount: whose mail the rule is abouttargetThe domain name, or the account address. It must be a domain this server hosts, or an account in one directioninbound(a message with a local recipient the rule covers),outbound(a message from an authenticated sender it covers), orbothjournal_addressWhere 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 modereportorcopyenabledA rule that is off copies nothing and keeps everything it said noteWhy 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
reportis the Exchange journal shape: amultipart/mixedmessage whose first part is the envelope report and whose second part is the original message attached asmessage/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
ToandCcheaders are what the sender typed; theRecipient:lines are where the message actually went, blind copies and expanded lists included. A message with no envelope sender - a bounce - writesSender: <>.copyis 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
copyis 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/messagesA 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 TOor 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.comto[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 Utilitiesmethods (GetJournalRules,SaveJournalRule,DeleteJournalRule)Windows only hmctl hmctl journalrule list\|create\|update\|deleteA client over the REST API PowerShell module Get-HmJournalRule,New-HmJournalRule,Set-HmJournalRule,Remove-HmJournalRuleOver the REST API Over REST, reading needs
view_configurationand writing needsmanage_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
scopedomain,targetthe domain name,directionboth,modereportand thejournal_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.
ArchiveDirandArchiveDomainskeep 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
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login