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
  • 0 Votes
    1 Posts
    132 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
    119 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) no longer signs mail from outside whose From names a domain hosted here with that domain's DKIM key, and signs an authenticated submission as another hosted domain only when its account may send as the address its From names (CVE-2026-107567); the upgrade stops the signing, and only rotating the domain's key, as this topic describes, ends the signatures already made. See the 6.3.7 announcement. Five things new in 6.3.4 keep your signing keys fresh or read how the world is answering the mail your domains send: DKIM key rotation, the DMARC and TLS reports your domains receive, the providers' complaint feedback loops, bounce classification with a suppression list, and a daily check of your own addresses against block lists. The one thing to know first: each is its own switch and each is off until you turn it on - the reputation watch is the exception, on in a new installation and off after an upgrade - and only one of them, the suppression list, ever stops a message. The DKIM and DMARC records themselves are in Being trusted by the servers you send to. The Control Panel and the COM API named below are Windows-only. The Control Deck, the REST API and hmctl work against a Windows or a Linux server; the Linux packages install hmctl, and the Windows installer does not carry it. DKIM keys that rotate Schema 6048. A rotation policy per domain does, on a schedule or on Rotate now, what used to be done by hand. It is off on every domain until switched on, and it never edits DNS: it says what to publish, checks it, and waits. The walk. Three slots, each a selector with a key file: current (the only key that signs), next (published and waiting; it never signs before the switch) and retiring (kept for mail still on its way). The schedule starts a rotation when the current key is older than the interval, or somebody presses Rotate now. An RSA key is generated under a new selector: the one you give, or s and today's UTC date (s20260915, then s20260915b if that is taken). You publish a TXT record at <selector>._domainkey.<domain> with the value v=DKIM1; k=rsa; p=.... Every pass - every fifteen minutes, and on Check DNS now - looks the record up. The first pass that finds exactly the new key records the moment. Any pass that does not find it matching sends that moment back to zero. Signing moves once the record has matched for the whole propagation period and the retiring slot is free. The old selector's record stays published for the retention period. The old selector is retired. A key file the rotation generated is deleted; one it did not generate is left. The status lists the record with the action remove, and removing it is yours. Signing itself is still the domain's DKIM signing switch: the rotation moves keys, it does not switch signing on. Setting REST key Default Range Rotate on a schedule dkim_rotation_enabled off Days between rotations dkim_rotation_interval_days 180 1 to 3650 Propagation period, hours dkim_rotation_propagation_hours 72 1 to 720 Retention period, days dkim_rotation_retention_days 7 1 to 365 Key size, bits dkim_rotation_key_bits 2048 2048, 3072 or 4096 Every interface refuses a value out of range rather than clamping it. Set them in the Control Panel's domain dialog (DKIM tab), the Deck's domain editor, PUT /api/v1/domains/{domain}, or hmctl domain update example.com --set dkim_rotation_enabled=true. Nothing is written to hMailServer.ini. What the status says. GET /api/v1/domains/{domain}/dkim/rotation answers the phase (off, idle, awaiting_dns, propagating, ready, waiting_for_retiring_slot) and each slot's last check: unchecked, matches, not_found, different, lookup_failed or key_unreadable. different means a record exists and none carries this key, and waiting will not fix it. A match proves that this server's resolver sees the key, not that every receiver's does. Where the keys are, and who can read them. <DataDirectory>/DKIM, as <selector>._domainkey.<domain>.pem. Windows Linux Default directory C:\Program Files\hMailServer\Data\DKIM /var/lib/hmailserver/DKIM Protection A protected security descriptor naming only SYSTEM, the Administrators group and the service account; it does not inherit the data directory's permissions Created with mode 0600, never widened On Windows, a Control Panel that is not elevated cannot read a generated key; the records it shows come from the status document. A managed domain refuses a blind save. While rotation is on, or a next or retiring selector is in flight, a save that would change the selector or key file is refused: over COM with an error, over REST with 409. When it goes wrong. The dkim.rotation.stalled alert, on by default and held for the daily digest, is raised when a next key's record has not been seen for the rule's threshold (7 days by default) or the last attempt to move a running rotation failed. A record that is missing or different is never an error. The error log is for faults, codes 6610 to 6615. A key that has been compromised. If an earlier old selector is still being kept, Retire the old key now first. Rotate now, and publish the new record at once. Check DNS now until the record matches, then Switch now with the box that skips the propagation period (hmctl dkim switch example.com --force). Force skips the period and never the check. Retire the old key now again, which deletes the compromised key's file if the rotation made it. Do not remove the old record. Publish it with an empty key, v=DKIM1; p=, which says the key is revoked rather than missing. Backups. A backup that includes the data directory includes the key files. Since 6.3.4 an archive can be encrypted (7-Zip AES-256, headers included), but encryption is off until a backup passphrase is set, and until then the archive holds the private keys in the clear: treat it as you treat the keys. Set it on the Control Panel's Backup & restore → Encryption, the Deck's Backup view, PUT /api/v1/settings/backup (encryption_passphrase) or hmctl backup passphrase set. Lose the passphrase and everything in every archive written with it is lost. Not done: generating Ed25519 keys; signing twice during the switch; publishing a revocation. The reports your domains receive Schema 6070. Receivers send a DMARC aggregate report once a day, and senders that honour TLS-RPT report on their sessions to your MX hosts. Name the mailbox. Each domain has one report mailbox: the Reports received tab of the Control Panel's domain dialog, the Reports the domain receives group of the Deck's domain editor, or report_mailbox on PUT /api/v1/domains/{domain}. It is empty by default, and empty means nothing is read. The address has to be in a domain this server hosts, and is refused otherwise. Then publish it: _dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]" _smtp._tls.example.com. TXT "v=TLSRPTv1; rua=mailto:[email protected]" Keep the p= your domain already publishes: this step adds only the rua= address. The wiki's DNS page says to start at p=none and tighten only once the reports show all your legitimate mail passes SPF and DKIM. What is read. A message is opened only when it is addressed to a report mailbox and to nothing else of this server's, and it is delivered to the mailbox unchanged, read or not. Each candidate part is unwrapped by its bytes (gzip, zip or plain), and the first character decides: < is a DMARC aggregate report, { a TLS-RPT report. The same report twice is one row. Nothing read here acts. No list is fed, no mail is refused, no setting changes. What carried a report is kept too. Nothing inside a report proves who sent it, so the dmarc= verdict and header.from= domain this server wrote on the carrying message are kept on the row (message_dmarc, message_from). Both are empty unless AuthenticationResultsEnabled is on; with it off, the reporter is shown as Not checked. The bounds. A message is not read past 32 MB, and an attachment is not inflated past 16 MB. A report keeps at most 2,000 records, and the log says when the chart under-counts. A domain takes in at most 20,000 records and 1,000 TLS-RPT rows in any twenty-four hours; a report past either ceiling is refused whole. The chart. GET /api/v1/domains/{domain}/dmarc-reports/summary?days=30, drawn by the Deck's DMARC page and hmctl dmarc summary: messages passed and failed, what receivers quarantined and rejected, the reporters, and up to 200 sources that sent as the domain. DomainReportRetentionDays (365 by default; 0 keeps for ever) decides how long a report is kept. The rows are not in the backup; the reports are still in the mailbox, which is. Not done: no reverse DNS or geography for a source; no forensic (ruf=) reports; no alert when the pass rate falls. The chart is how you see whether it is safe to move from p=none to p=quarantine to p=reject. Complaints: the feedback loops Schema 6055. The large mailbox providers post an abuse report (RFC 5965) to an address you registered with them whenever a reader presses this is spam. With processing on and a mailbox named, a report is recorded against the sending domain and account, counted, and past a threshold puts the address on the domain's suppression list. It is off by default, and inert without a mailbox. Why only a configured mailbox is an intake. A feedback report carries nothing known only to the provider. What tells a genuine report from a forged one is that it arrived where the provider was told to send it. So use a dedicated address the loops know and nobody else does - [email protected] rather than [email protected] - and do not publish it. Setting REST key Default Meaning ComplaintProcessingEnabled processing_enabled off The whole feature ComplaintMailbox mailbox empty The address the loops are registered against, or several separated by commas. Each must be one this server hosts ComplaintSuppressionThreshold suppression_threshold 0 0 to 100. 0 records and counts every report and lists nobody. Raise it to 1 once the entries have been read ComplaintRetentionDays retention_days 180 0 keeps for ever; at most 3650. It does not remove a suppression entry a complaint made They are keys of /api/v1/settings/complaints, a group on the Deck's Settings page, and hmctl settings get complaints. Three gates that are not settings. The report must be addressed to the complaint mailbox and to nothing else. The domain it is about must be one this server hosts. And an Original-Rcpt-To that this server hosts lists nobody. A complaint is not a bounce. It has a counter of its own, hm_bouncecounts.bccomplaints. Its list entry carries source complaint, and the refusal a sender meets uses 5.7.1, never 5.1.1. not-spam is recorded and counts towards neither the rate nor the list; every other feedback type counts. It is the same suppression list the bounces feed, and the list is applied whenever either feature is on. The rate is complaints divided by messages delivered, over a seven-day window that is not a setting, in parts per million. A domain that has sent fewer than 100 messages in the window is not judged. The complaints.rate.high alert ships on, seeded at 1000, with a cooldown of a day, held for the digest. With MetricsPerDomainEnabled set, the gauge hmailserver_domain_complaint_rate{domain} is exposed with two others. Reading them. The Deck's domain row has a Complaints button; GET /api/v1/domains/{domain}/complaints needs view_configuration and view_message_trace; hmctl complaint list --domain example.com and hmctl complaints rates do the same from a shell. A backup does not carry the complaints. Bounces read, and a suppression list Schema 6049. With bounce processing on, every refusal met while delivering and every RFC 3464 delivery report that arrives later is classified against its enhanced status code and counted per recipient and per sending domain. An address that hard-bounces as many times in a row as the threshold says goes on that domain's suppression list. It is off by default. Setting REST key Default BounceProcessingEnabled processing_enabled off BounceHardBounceThreshold hard_bounce_threshold 1 (1 to 100) BounceMailboxFullIsHard mailbox_full_is_hard off BounceSuppressionAction suppression_action 0 refuse; 1 drop; 2 observe BounceSuppressionExpiryDays suppression_expiry_days 0, entries kept until removed BounceReadDeliveryReports read_delivery_reports on BounceCountRetentionDays count_retention_days 90 BounceReportWindowDays report_window_days 7 (1 to 30) They are on the Control Panel's Delivery of e-mail page (the Bounces and the suppression list card), the Deck's Settings page, /api/v1/settings/bounces and hmctl settings get bounces. The safe way to start is suppression_action=observe for a week. The server classifies, counts and lists, delivers everything, and logs every message that would have been stopped. Read the list, then switch to refuse. What counts as which. Code Category Counts towards the list 4.2.2 or 5.2.2 mailbox_full only with BounceMailboxFullIsHard any other 4.x.x soft no 5.7.x policy never 5.1.1, 5.1.2, 5.1.3, 5.1.6, 5.1.10, 5.2.1 hard yes any other 5.x.x other no A bare 550 is not a dead address. The classifier never believes a code this server worked out for itself. When a remote's reply carries no enhanced code of its own, the words decide, policy words first: 550 Recipient address rejected: Access denied is policy, 550 No such user here is hard, and 550 <[email protected]> is other. hmctl bounces classify "550 5.1.1 User unknown" shows how a reply would be read, and changes nothing. A delivery report has to prove itself. It is counted only when it passes six checks, among them: its envelope sender is null, it was not classified as spam, it arrived over SMTP here and not from a signed-in user, and it reproduces a row of hm_bouncereportkeys, a SHA-256 of the Message-ID, recipient and envelope sender of a message this server really sent. What that does not stop: one recipient in To or Cc could forge a report for another visible recipient. Such an entry says source: report, and read_delivery_reports can be turned off. What the list does. Action At RCPT TO and on the webmail At delivery refuse Refused with 550 and the status that listed the address, with a sentence saying why The recipient is removed and returned to the sender in an ordinary non-delivery report drop Accepted Removed silently; a log line says so observe Accepted Delivered; a log line says it would have been stopped Mail already in the queue meets the list at its next attempt. If the list cannot be read, the message is sent as if the address were not listed, and HM6622 is reported at most once an hour. Looking at it. Control Panel → Monitoring & troubleshooting → Suppression list; Deck → Domains → Suppressions ›; GET /api/v1/domains/{domain}/suppressions; hmctl suppression list|create|delete --domain. The list's document says processing_enabled when bounces are read and list_applies when the entries act. Removing an entry lets the next message through at once and starts the run of hard bounces again from none. The reputation watch Schema 6057. Once a day the server asks the block lists about the addresses it sends from, and raises reputation.listed when one is listed. On in a new installation, off after an upgrade: an upgraded server may already have a delivery bind address, and switching this on beneath it would send that address to four third parties every day without anybody choosing it. Turn it on with reputation_watch_enabled, on the Deck's Settings page, PUT /api/v1/settings or hmctl settings set; switching it off clears a standing reputation.listed. Which addresses. SMTPDeliveryBindToIP, the bind to address action of any active global rule, and ReputationWatchAddresses, which exists for a server behind NAT. A private, loopback, link-local or shared address is never looked up. At most 32 addresses are checked; one past that is reported as too_many. Which lists. ReputationWatchLists ships with ipv4:zen.spamhaus.org, ipv4:bl.spamcop.net, ipv4:psbl.surriel.com and ipv4:bl.mailspike.net. Nothing shipped covers IPv6, on purpose: an IPv6 outbound address reads not_applicable against all four until you name a zone whose IPv6 coverage you have read. A subscriber key put in this setting is readable by everybody who can read the configuration. A refusal is not a listing, and neither is clean. Before any address is looked up, each list is asked about 127.0.0.2, which must be listed, and 127.0.0.1, which must not be. What comes back Status An answer in 127.255.255.0/24 refused The lookup failed lookup_failed 127.0.0.2 is not listed list_not_answering 127.0.0.1 is listed list_lists_everything An answer outside 127.0.0.0/8 unexpected_answer If DNSServer is one of the large public resolvers, Spamhaus will be refused every day, and the report says so rather than calling the server clean. Point DNSServer at a resolver of your own. A lookup that fails never clears a listing. After a week with no definite answer a pair becomes unknown, not clean: hmctl still exits 1, and the report answers listed as null. Where and when. GET /api/v1/reputation; POST /api/v1/reputation/check, answered 409 while a check is running; the Deck's Outbound reputation view; hmctl reputation status. A check is bounded to 120 seconds; pairs it does not reach are written as lookup_failed. The alert is sent at once rather than held for the digest. Not done: delisting; domain-based lists; watching between checks; seeing behind a smart host. Where to read more Operator documents at the v6.3.4 tag: DkimKeyRotation.md, DmarcReports.md, Complaints.md, BounceSuppression.md and ReputationWatch.md. Wiki: DNS for Your Domain, Outbound Shaping and Monitoring and Health. Public documentation: 8.5 DKIM and 8.6 DMARC. On this forum: Being trusted by the servers you send to, The Control Deck, Requiring TLS for the reports your server sends, and Alerts, the audit trail, syslog for how an alert reaches you.
  • 0 Votes
    1 Posts
    150 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) reads a DNS lookup that fails as a failure, not as a name with no addresses: the HELO and PTR tests no longer score a name they could not look up, a DNS blacklist and SURBL no longer read a failed lookup as "not listed", and a DKIM selector that is a CNAME whose query failed is TempFail, not PermFail. SURBL reads a link's host that is not ASCII by UTS 46 and an HTML part's character references as a browser does, and SpamAssassin is sent a collected message's own Return-Path, so its SPF and envelope rules may score such mail differently. The DMARC check's pass now also decides whether a forward of mail from outside whose From names a hosted domain's organisation is given an envelope sender in that organisation, and no pass is recorded for a message the check did not judge: DMARC off, a sender on the white list or an IP range with spam protection off, a message over AntiSpam.MaximumMessageSize, or one the tests before it had already scored to the higher threshold. In the Control Panel the sidebar has a Spam filtering group, with its six allow and block lists under Lists, and a Virus scanning group, and Ctrl+K takes every old title to its page. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) lets Rspamd learn from the Junk folder beside SpamAssassin: a message a user moves into Junk is taught as spam, and one moved out as ham, to the filter MoveLearningBackend names, SpamAssassin by default and taught as before, and nothing is sent to Rspamd until it is chosen and its controller and classifier are set (JunkFolderLearning.md). See the 6.3.8 announcement. This topic walks through every layer hMailServer 6.3.4 puts between an SMTP session and a mailbox: the two thresholds, the twelve tests in the order they run, greylisting and its 6.3.4 hardening, the DNS and URL blocklists, SPF, DKIM, DMARC and ARC, SpamAssassin, and the quarantine. The one thing to know first: none of these tests is a verdict on its own. Each failing test adds a score, two thresholds decide what the total means, and three kinds of session are never tested at all: an authenticated session, a sender or IP on the anti-spam white list, and any session from an IP range with spam protection switched off. Where the decisions are made Everything happens in one of two moments: before the body (at MAIL FROM or RCPT TO) and after the body (after DATA or the last BDAT). The pre-transmission tests run at MAIL FROM by default: DNSBLChecksAfterMailFrom defaults to 1, and 0 moves them to the first RCPT TO, which leaks less to an address-probing client but accepts more of the conversation first. A session from a configured incoming relay has both phases deferred to after DATA. Who is never tested. SMTPConnection::GetDoSpamProtection_ returns false the moment the session is authenticated, so your own users' outbound mail is never scored; the sending limits and rate limits are the controls for that. A sender or IP on the anti-spam White list skips the tests entirely, not merely the scoring: SPF is not evaluated, no Authentication-Results verdict is recorded and no reason header is written. The read-only overview. Spam & virus filtering → Spam filtering overview in the Control Panel (Windows only) shows on one screen which checks are on, what each scores, what the thresholds then do, and which checks are switched on but cannot run. The score and the two thresholds Both thresholds are on Spam & virus filtering → Anti-spam settings → General. Setting Default At or above it 0 means AntiSpam.SpamMarkThreshold 5 Delivered but tagged: X-hMailServer-Spam: YES, X-hMailServer-Reason-* headers and, optionally, a subject prefix Never mark AntiSpam.SpamDeleteThreshold 20 Refused during the conversation, or quarantined if quarantine is on and the verdict came after the body Never refuse AntiSpam.MaximumMessageSize 1024 KB Post-transmission tests are skipped for a larger message No limit of its own; the MIME parser's 80 MB ceiling still applies The higher of the two thresholds is also the early-out: SpamTestRunner::RunSpamTest stops calling tests once the running total reaches it, which is why a message condemned by the blocked-senders list never pays for a DNS lookup. With both thresholds at 0 the early-out is skipped, because greylisting's SPF bypass needs the SPF result and SPF is the sixth test. With AddHeaderSpam and AddHeaderReason both on (the defaults), a marked message carries: X-hMailServer-Spam: YES X-hMailServer-Reason-1: Blocked by SPF. (example.com does not designate 203.0.113.9) - (Score: 3) X-hMailServer-Reason-2: Rejected by Spamhaus. - (Score: 3) X-hMailServer-Reason-Score: 6 Subject: [SPAM] Your invoice Only failing tests get a Reason-N line. X-hMailServer-Reason-Score is the sum of the failing tests, and with AddHeaderReason off it is not written at all. The subject prefix is off by default; its text is [SPAM] (antispamprependsubjecttext). Start by tagging, not deleting. The wiki's advice is a fortnight with the delete threshold at 0, reading what got tagged, before turning deletion on well above anything legitimate has scored. Every test, in the order it runs This is the registration order in SpamTestRunner::LoadSpamTests; nothing reorders it at runtime. Tests 1 to 6 run before the body, 7 to 12 after it. # Test Switched on by Default Default score 1 Blocked senders a non-empty Blocked senders list list empty 100 on a new entry 2 DNS blacklists an active row on DNS blacklists two rows, both inactive 3 3 HELO host AntiSpam.CheckHostInHelo off 2 4 PTR record AntiSpam.CheckPTR off 1 5 Sender MX AntiSpam.UseMXChecks off 2 6 SPF AntiSpam.UseSPF off 3 7 SURBL an active row on SURBL servers one row, inactive 3 8 DKIM AntiSpam.DKIMVerificationEnabled off 5 9 ARC AntiSpam.ArcFilteringEnabled, a trusted-sealer list and DMARC on off minus the DMARC failure score 10 DMARC AntiSpam.DMARCEnabled on 5 11 SpamAssassin AntiSpam.SpamAssassinEnabled off 5, or the merged score 12 Filter hook a non-empty FilterHookUrl empty the engine's score; 100 for a reject DMARC is the only test on by default, and at 5 a DMARC failure marks a message without refusing it. That is the shipped posture: tell the recipient, do not destroy the mail. What makes a test fail matters as much as its score. SPF scores only a hard Fail. DKIM scores only PermFail; a message with no signature is None, not a failure. DMARC scores a p=reject or p=quarantine policy the message failed and, separately, a message with more than one From header field; p=none failures are logged and score nothing. The blocked-senders list never matches the null sender, and its 550 says only "Sender address is blocked." without naming the entry. Every DNS-backed check fails open: a lookup that does not complete is read as "not spam". Since 6.3.7 a lookup that fails is a failure, not a name with no addresses: the HELO and PTR tests no longer score a name they could not look up, a DNS blacklist and SURBL no longer read a failed lookup as "not listed" (the blacklist says so in the TCP/IP log and, at most every 15 minutes, the application log), and a DKIM selector that is a CNAME whose query failed is TempFail, not PermFail, so it no longer scores. Greylisting Greylisting is not a score. It is a temporary refusal of a triplet (sender address, recipient address, connecting IP) this server has not seen before. The settings are on Anti-spam settings → Greylisting. Setting Default Meaning AntiSpam.GreyListingEnabled off Master switch AntiSpam.GreyListingInitialDelay 30 minutes How long a new triplet is refused for AntiSpam.GreyListingInitialDelete 24 hours An unconfirmed triplet is forgotten after this AntiSpam.GreyListingFinalDelete 864 hours (36 days) A confirmed triplet's expiry, pushed forward on every accepted message AntiSpam.BypassGreylistingOnSPFSuccess on Needs the SPF test enabled and to have run AntiSpam.BypassGreylistingOnMailFromMX off Costs an A and an MX lookup per message A greylisted recipient gets 451 Please try again later at RCPT TO; when the envelope sender is empty the refusal is postponed to the end of DATA and answered 451 (docs/Greylisting.md; the wiki prints 450), because an empty sender at RCPT TO is very often another server's callout. Greylisting is skipped for a recipient domain with Enable greylisting for this domain unticked, for an IP on the greylisting white list, and for a sender or IP on the anti-spam white list. The wiki records that GreylistingEnabledDuringRecordExpiration=0 did nothing at 6.2.28, so greylisting stays in force during the expiry sweep whatever it says. Hardened since 6.3.4 (schema 6072; docs/Greylisting.md The triplet key no longer truncates. Addresses used to be stored cut to 200 characters, so two long addresses sharing their first 200 were one triplet. Each is now keyed by an MD5 digest of the whole address with its domain lower-cased. This is the one change at the defaults: on PostgreSQL, and on MySQL with a binary collation, [email protected] and [email protected] were two triplets and are now one, so a sender waits once less, never once more. Old rows are not rewritten at upgrade. A sliding wait and an abuse lock, both off. With GreyListingSlideOnEarlyRetry on, each retry before the window opens moves the window to now plus the initial delay. GreyListingAbuseThreshold (0; the operator who asked for it uses 20) is the attempt answered 550 5.7.1 Too many delivery attempts before the greylisting delay had passed. ..., with the triplet locked for GreyListingAbuseLockMinutes (60). It counts attempts, not retries: a server delivering twenty queued messages to one recipient down one connection makes twenty attempts in seconds. A triplet that has passed once is never refused. Clear greylisting triplets lifts every lock, at the cost of greylisting every sender once more. The greylisting bypass list (/api/v1/greylisting-bypass, table hm_greylisting_bypass a domain such as example.com, or a suffix such as .mail.example.com, matched on the envelope domain, the HELO host or either, passes greylisting only when SPF, DKIM or DMARC passes for that name. At least one requirement is needed. An entry requiring DKIM or DMARC cannot be judged at RCPT TO, so its greylisting runs after DATA, where a 451 or 550 ends the transaction for every recipient. The test an entry requires must be switched on: an entry requiring DKIM while DKIM verification is off (the default), or DMARC while DMARC is off, can never be satisfied, so the message is greylisted at RCPT TO as if the entry were not there; for an entry matched on the HELO, the SPF check of the HELO name is made only while SPF is switched on. It skips greylisting only; every spam test still runs. BypassGreylistingOnSPFSuccess, by contrast, covers every SPF pass in the world, a hijacked mailbox at a large provider included. DNS blacklists and SURBL Spam & virus filtering → DNS blacklists seeds two rows, both inactive until you tick them: DNS host Expected result Reject message Score zen.spamhaus.org 127.0.0.2-8\|127.0.0.10-11 Rejected by Spamhaus. 3 bl.spamcop.net 127.0.0.2 Rejected by SpamCop. 3 The expected-result field takes | for alternatives, a range on the last octet and wildcards; a malformed range is error HM5342. Every lookup, match or not, is written to the TCP/IP log, which is how to prove a listing to a sender who disputes it: DNS lookup: 9.113.0.203.zen.spamhaus.org, 1 addresses found: 127.0.0.4, Match: 1 The wiki warns that public mirrors of the large lists rate-limit or refuse resolvers that use a public DNS service; if a list stops matching, check its operator's terms first. SURBL checks the links in the body, so it runs after DATA. Collection stops once more than 15 distinct addresses are found, the run for one list aborts after 10 seconds, and, to 6.3.6, the first failed lookup abandoned the rest of the list; since 6.3.7 a failed lookup is a failure rather than "not listed", and SURBL goes on to ask the message's other names. One row is seeded inactive: multi.surbl.org, "Rejected by SURBL.", score 3. SPF, DKIM, DMARC and ARC SPF (AntiSpam.UseSPF, off, score 3). Evaluation is bounded at 10 DNS-querying terms and at SpfVoidLookupLimit void lookups (default 2; 0 switches that limit off). Since 6.3.4 the evaluator follows RFC 7208, ported from upstream hMailServer, in place of the RFC 4408 library the server carried: 6.3.3 and earlier Since 6.3.4 DNS Its own lookups; DNSServer, the cache and the timeout were ignored on Windows The server's own resolver settings apply Linux Every check was TempError (HM6406), so DMARC lost its SPF half Works Loopback clients 127.0.0.0/8 and ::1 passed before any policy was read No exemption Authentication-Results Everything but pass and fail written as neutral Each of the seven results under its own keyword Nothing is scored differently: only a fail scores, as before. The loopback row is the one to check when upgrading. CHANGELOG says that where the SPF test or DMARC runs for a loopback client, mail from a domain whose policy does not name the address is judged like any other, so a content filter that re-injects mail should authenticate, or connect from an IP range with anti-spam off. DKIM (AntiSpam.DKIMVerificationEnabled, off, score 5). RSA keys shorter than 1024 bits are refused outright, and rsa-sha1 signatures are invalid unless DkimAcceptSha1=1. DMARC (AntiSpam.DMARCEnabled, on, score 5). More than one From field is scored before any alignment is computed, because DKIM signs the last From while the header reader answers with the first. ARC (AntiSpam.ArcFilteringEnabled, off; AntiSpam.ArcTrustedSealers, empty). Its only output is an offset of exactly minus the DMARC failure score, applied when a chain whose every sealer is trusted validates and DMARC is about to fail. With the list empty it does nothing, deliberately: anyone can fabricate a chain and seal it with their own keys. Matching is exact. Since 6.3.4: policy the score cannot express (schema 6073; all off by default; docs/SmtpPolicy.md Setting What it does UseSPFSoftFailScore, UseSPFTempErrorScore, UseSPFPermErrorScore Scores of their own for SoftFail (~all), TempError and PermError. 0 is "not scored", as before SPFTempErrorDefer Answers a TempError with 451 4.4.3, so a resolver blip defers the message. Never a 451 on PermError RejectMultiRcptNullSender A null sender's second and every later distinct recipient, and its DATA, are refused 550 5.7.1. A single-recipient null sender is never touched BanMultiRcptNullSender, BanMultiRcptNullSenderHours Also bans the address through the auto-ban, once per session, for 24 hours by default (1 to 8760); never an address on AutoBanNeverBan or an incoming relay. Does nothing while RejectMultiRcptNullSender is off The domain white and black list (Spam & virus filtering → Domain white and black list; /api/v1/domain-policy-list) is keyed by the envelope domain or the HELO suffix rather than by IP, for the correspondent whose sending hosts change every week. It is consulted at MAIL FROM for unauthenticated sessions of a range that runs the spam tests. Black is 550 5.7.1 Mail from <name> is refused by this server's policy. before anything else runs. White skips greylisting, is not deferred for a TempError and is not refused or quarantined on the score, while every test still runs and the mark threshold still marks. It never overrides authentication. The list is ASCII, so a U-label such as bücher.example is refused, and hmctl has no verb for it. SpamAssassin and the filter hook hMailServer speaks the spamd protocol over TCP to any SpamAssassin daemon, on this machine or on a Linux host. Set AntiSpam.SpamAssassinHost (127.0.0.1) and AntiSpam.SpamAssassinPort (783) and press Test SpamAssassin connection. AntiSpam.SpamAssassinMergeScore (off) uses the integer part of SpamAssassin's own score= in place of the fixed AntiSpam.SpamAssassinScore (5). The timeout moves between SAMinTimeout and SAMaxTimeout (30 and 90 seconds) with load. SpamAssassinLearnOnMove (off) tells spamd that a message moved or copied into Junk is spam, and one moved out of it ham, with spamc's TELL, which spamd refuses unless it was started with --allow-tell. An unresolvable host is HM5507; a scan that does not finish in time is HM5508, and the message is accepted with no verdict; a message that cannot be reloaded after the scan is HM5509 and is left as SpamAssassin wrote it. FilterHookUrl puts rspamd, or anything answering JSON over HTTP, in the path of every accepted message; http://127.0.0.1:11333/checkv2 needs no adapter. FilterHookTimeoutSeconds is 10, and with FilterHookFailClosed (off) turned on an unanswered check scores FilterHookRejectScore (100) and reports HM5541. Quarantine, and what a refused sender sees Quarantine (QuarantineEnabled, off; QuarantineRetentionDays, 30) changes what the delete threshold does after the body: the message is answered 250 Queued for delivery and held on Spam & virus filtering → Quarantine. A verdict reached before the body has no message to hold, so it stays a refusal, and a quarantine that fails to store falls through to refusing, so a spam refusal never becomes silent deletion. Since 6.3.4 the quarantine also holds what a mail flow rule sends there (Rule: <name>), what impersonation protection holds and what a recipient's blocked-senders list holds. Per-account overrides, on the account dialog's Spam tab, re-judge that account's own copy at delivery. They only read X-hMailServer-Reason-Score, and only while Add X-hMailServer-Reason header is on; with it off the value could be the sender's own, so the overrides decline to act. Moment Client sees Score over delete threshold, before the body 550 5.7.1 <text of the first failing test> Score over delete threshold, after the body 554 5.7.1 <text of the first failing test> Same, but quarantined 250 2.0.0 Queued for delivery Greylisted, real sender 451 Please try again later. Greylisted, null sender 451 at end of DATA (the wiki prints 450) Per-test timings go to the debug log (Spam test: SpamTestSPF, Score: 3, Time: 41 ms); a test taking 10 seconds or more is promoted to the application log. Because every DNS-backed check fails open, a resolver that has stopped answering looks like a run of clean mail, so DKIM, DMARC and SURBL, and since 6.3.7 a DNS blacklist, each write one application-log line at most every 15 minutes when their lookups stop completing. A configuration that works The wiki's order for a small server: Leave the thresholds at 5 and 20 and leave DMARC on. Watch for a fortnight. Activate zen.spamhaus.org (score 3). Turn on SPF (score 3) and DKIM verification (score 5). With a DNSBL hit they reach 11, still under the delete threshold. Turn on AuthenticationResultsEnabled, so every delivered message records what the server concluded. Add greylisting if your users can tolerate a first-message delay, leaving bypass on SPF success on. Only then add SpamAssassin or an external engine, and consider raising the delete threshold rather than lowering it. What this topic does not cover, and where to read more The per-recipient allow and block lists, the quarantine digest, spam traps, country blocking and impersonation protection are in allow and block lists, digests and spam traps, and viruses in viruses and dangerous attachments. The other direction is Being trusted by the servers you send to. The Control Panel and COM are Windows-only; the 6.3.4 switches are also on the Settings view of the Control Deck and PUT /api/v1/settings/antispam. Wiki: https://gitlab.com/hmailserver/hmailserver/-/wikis/Stopping-Spam https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Greylisting.md https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SmtpPolicy.md Public documentation: https://www.progressiverobot.com/hmailserver-documentation/#10-stopping-spam and https://www.progressiverobot.com/hmailserver-documentation/#265-the-anti-spam-pipeline The 6.3.4 security announcement and Upgrading to 6.3.4
  • 0 Votes
    1 Posts
    144 Views
    ProgressiverobotP
    This topic takes a freshly installed hMailServer 6.3.4 to its first working mailbox, proves it locally, and then lists the DNS records without which its mail either does not arrive or is filed as spam. The one thing to know first: the domain's Postmaster address box is the catch-all, not the postmaster@ mailbox the email standards ask for. Create postmaster@ as an account, and leave that box empty unless you want every misaddressed message accepted and stored. The paths below are the Windows Control Panel's; on Linux, which has no Control Panel, the same is done in the Control Deck or over the REST API. If you are learning, make the sample domain first (since 6.3.4) One button, on the Control Panel's Welcome page and on the Control Deck's Checklist view, or one command (hmctl sample-domain create, hmailserver --sample-domain create on Linux, POST /api/v1/setup/sample-domain) makes a throwaway example.test: three mailboxes, a distribution list of the three, one rule and eight delivered messages. The same button, or ... remove, takes it away and says what it removed; a domain called example.test that it did not make is never touched. All three mailboxes sign in with one password, which the server mints when it makes the sample: twenty characters of Crockford base32, grouped in fives. It is shown once, in the answer to the call that made the sample, and is stored nowhere, so write it down; if you lose it, remove the sample and make it again. Older wiki text that speaks of "one documented password" describes an earlier version of the feature and is out of date. Remove the sample before the server takes real mail. Its mailboxes are ordinary accounts that can sign in and relay. Add your domain Go to Accounts & domains → Domains, type the name and press Add domain, then select it and press Properties. On Linux, use the Deck's Domains view, or: curl -u Administrator -H "Content-Type: application/json" \ -d '{"name":"example.com","active":true}' \ http://127.0.0.1:8045/api/v1/domains postmaster is optional in that body and is left out here, because it is the same catch-all field. The call answers 400 if the name is not a valid domain name and 409 if it already exists. Since 6.3.4 the Control Panel's domain editor has fourteen tabs; the seven that 6.3.4 added are each off, or empty, until set. The fields that matter on the first day: Tab Field Default What happens when it is wrong General Domain enabled ticked Unticked: every RCPT TO is answered 550 Domain has been disabled. General Domain name The part after the @. Setting it to the host name (mail.example.com) is the classic first-day mistake General Postmaster address empty The catch-all. Set: no address in the domain can be refused. Empty: unknown recipients get 550 Unknown user Limits Maximum message size (KB) 0 0 falls back to the server-wide maxmessagesize, seeded at 20480 KB Limits Enable plus addressing off, + Off: alice+invoices@ is an unknown recipient Relay Relay host empty Where mail from this domain leaves through. This is not a route DKIM Enable signing off Enabling before the DNS record is live makes every message fail DKIM at the receiver In RecipientParser::CheckDeliveryPossibility, an address that matches no account, alias, distribution list or route falls through to the postmaster address and the resolution restarts with it. That is why the box is a catch-all. Add a mailbox Select the domain, type the address and a password under the account list, and press Create account; then Edit for the full editor. Field Value to use Notes Password Press Generate strong password 16 characters from a cryptographic RNG, shown once and copied to the clipboard Administration level Normal user Server administrator can manage everything. Give it only to yourself Quota (MB) 0 for unlimited Account enabled ticked Unticked: 550 Account is not active. at RCPT TO On Linux, make the account on the Control Deck or with POST /api/v1/domains/{domain}/accounts. Create at least [email protected] and your own mailbox. A full mailbox is refused during the SMTP conversation, not bounced: with RejectFullMailboxAtRcpt on, as it is by default, RCPT TO is answered 452 4.2.2 Mailbox is full., a temporary refusal, so the sending server holds the message and no backscatter is sent. Since 6.3.4, accounts can be imported from a CSV file over REST, with hmctl and with the PowerShell module, with a dry run that changes nothing; and an identity provider can make and remove them over SCIM. Three things about an account are set only on the Control Deck and over REST, not in the Control Panel: its kind (a person, a room or equipment), encryption at rest, and a webhook route. How an address is resolved Every RCPT TO is answered by one function, in a fixed order that ends at the domain's postmaster address; the full order, with domain aliases, plus addressing and the webhook route, is in Aliases, distribution lists and public folders. What it means on the first day: Question Answer Why did mail to a mistyped address get delivered? The domain has a postmaster address, so it fell through to the catch-all Why does alice+anything@ not work? Plus addressing is off by default and is per domain Why does an alias to an external address still count as local? Only at the first hop, so that it is treated as inbound mail and not as relaying Test it locally, before touching DNS The test below works because a fresh database's My computer IP range lets the server itself, on 127.0.0.1, send as one of its own domains without a password, while anyone may send you mail and nobody may relay through you without logging in. A refused direction is 550 Delivery is not allowed to this address.; one that needs authentication is 530 SMTP authentication is required. The two seeded ranges and what each allows are in IP ranges, auto-ban, lockout and sending limits. Send-MailMessage -SmtpServer localhost -Port 25 ` -From "[email protected]" -To "[email protected]" ` -Subject "First test" -Body "If you can read this, hMailServer is working." Or by hand with telnet localhost 25. The replies worth recognising: Line What it tells you 250-SIZE 20971520 The server-wide maxmessagesize, 20480 KB, in bytes 250-STARTTLS Present only when the port's connection security is STARTTLS optional or required 250 AUTH … Absent when the port requires TLS, or the connecting range requires TLS when authenticating 250 2.0.0 Queued (0.016 seconds) The message is on disk and in the database 250 2.0.0 Queued for delivery Not the same reply: the message was quarantined Then check Monitoring & troubleshooting → Delivery queue, which should be empty, and look on disk: a delivered message is one file, C:\Program Files\hMailServer\Data\yourcompany.com\you\a3\{a3f01e6c-....eml}. Its first Received header reads with ESMTP, and with ESMTPSA for an authenticated submission over TLS. The DNS records your domain needs Do them in this order. Replace yourcompany.com and 203.0.113.10 with your own. Record Example Without it A / AAAA mail.yourcompany.com. IN A 203.0.113.10 Nothing resolves; the MX points at nothing MX yourcompany.com. IN MX 10 mail.yourcompany.com. Inbound mail bounces as "no mail server for this domain" PTR 10.113.0.203.in-addr.arpa. IN PTR mail.yourcompany.com. Google and Microsoft refuse or spam-folder outbound mail SPF yourcompany.com. IN TXT "v=spf1 mx a:mail.yourcompany.com ~all" Anybody can forge your domain; DMARC cannot pass on the SPF side DKIM dkim._domainkey.yourcompany.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." Forwarded mail fails everything, because forwarding breaks SPF DMARC _dmarc.yourcompany.com. IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]" No policy and no reports A / AAAA and MX. Add an AAAA only if the machine has a public IPv6 address that answers. An MX must point at a host name, never an IP address, and that name must not be a CNAME. The MX name is what MTA-STS matches, what TLSA records hang off, and what the certificate on port 25 should be issued for. PTR. Only your ISP or hosting provider can set it. It should name the same host as your HELO/EHLO, which hMailServer takes from Connections & protocols → Protocols → SMTP, Host name (HELO/EHLO greeting), falling back to the Windows computer name when blank. The Control Panel's DNS records page does not check it, and says so. SPF. Start with ~all; move to -all once the DMARC reports have shown you every system that sends as you. Publish exactly one SPF record per name: two beginning v=spf1 is a permanent error at every receiver. RFC 7208 caps the DNS-querying mechanisms at ten. In the other direction, checking SPF on incoming mail as a scored test is off by default (usespf is seeded 0), but DMARC checking is on by default and evaluates SPF and DKIM itself. DKIM. On the domain's DKIM tab click Generate key pair…: it creates an RSA-2048 key, saves the private key and shows the exact record, with a Copy DNS value button. dkim is the default selector. Switching signing on is gated on the published record matching. Ed25519 is supported too, with a key you supply. Since 6.3.4 a rotation policy per domain generates the new key under a dated selector (s20260915), checks the record every fifteen minutes and switches signing only once it has carried that key for a propagation period. It never edits DNS, and it is off until switched on. DMARC. Start with p=none, read the reports for a few weeks, then tighten to p=quarantine and p=reject. Do not start at p=reject. The DNS records page pre-fills rua= with the domain's postmaster address and omits the tag when there is none. Since 6.3.4, name that mailbox as the domain's report mailbox and the server parses the DMARC and TLS-RPT reports that arrive there and charts them on the Control Deck's DMARC page. Nothing read there acts on any mail. Optional hardening, at any point afterwards: Record Name Notes MTA-STS _mta-sts.yourcompany.com. IN TXT "v=STSv1; id=20260908120000" hMailServer serves the policy itself once WebServicesHttpsPort is set and the listener has a certificate for mta-sts.yourcompany.com; MtaStsPolicyMode defaults to enforce, so set it to testing to start TLS-RPT _smtp._tls.yourcompany.com. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]" Asks other servers to tell you when TLS to you failed TLSA _25._tcp.mail.yourcompany.com. IN TLSA 3 1 1 <sha256 of the certificate public key> Only meaningful if your zone is DNSSEC-signed. Re-publish before you rotate a certificate, never after CNAME autoconfig, autodiscover, mta-sts → mail.yourcompany.com. For client autoconfiguration and the policy host A / AAAA / CNAME openpgpkey.yourcompany.com Since 6.3.4, only if you publish OpenPGP keys BIMI is read, not published: since 6.3.4 the webmail shows a sender's logo, but this server publishes no BIMI record for your domains. Checking your work Give DNS an hour to propagate. Monitoring & troubleshooting → DNS records lists, per domain, the exact SPF, DKIM, DMARC, MTA-STS and TLS-RPT records to publish, with a Check button per record: Verdict What to do Not published Publish it Published but different Compare the two; the page shows what is there Published and correct Nothing Lookup failed A resolver or network problem, not a DNS-content one. Retry On Linux the Control Deck has a DNS records view. On either platform the first-hour checklist (since 6.3.4) judges the spf, dkim and dmarc items itself, for the first twenty active domains. From a command line: Resolve-DnsName yourcompany.com -Type MX Resolve-DnsName yourcompany.com -Type TXT Resolve-DnsName dkim._domainkey.yourcompany.com -Type TXT Resolve-DnsName _dmarc.yourcompany.com -Type TXT Resolve-DnsName 10.113.0.203.in-addr.arpa -Type PTR Being trusted by the servers you send to takes deliverability further. The ports your users will need The create scripts seed four listeners, SMTP 25 and 587, POP3 110 and IMAP 143, with no encryption. Since 6.3.4 a new installation sets all four to STARTTLS optional with the self-signed certificate and requires TLS for authentication from the Internet IP range; an upgraded server is left as it was (Hardening a public mail server). Ports 465, 993 and 995 are not created for you: add them on Connections & protocols → TCP/IP ports once you have a certificate. A TLS port with no certificate selected does not start (HM5113, Error initializing SSL. Certificate not set.); a port already in use is HM4316. Which connection security each port should have, and why STARTTLS optional belongs on port 25 alone, is in Connecting a mail client; which ports to open in the firewall is in Every port hMailServer listens on. Before you tell anyone the address postmaster@ exists as a real account. MX, SPF, DKIM and DMARC are published and verified. A certificate is installed and the client ports created: Let's Encrypt certificates are automatic. The administrator password is a PBKDF2 one. 6.3.4's installer writes it so; the wiki's checklist item about changing it from "the installer's MD5" describes 6.3.3 and earlier. A backup that you have restored once. What it does not do, and where to read more Nothing here publishes a DNS record for you: the Control Panel builds the text and checks it, and the DKIM rotation never edits DNS. The PTR record is your provider's to set. More on the records above: DKIM rotation, DMARC and TLS reports, and Requiring TLS for MTA-STS, TLS-RPT and DANE. The webmail and browser administration are separate topics: The webmail portal at /portal and The Control Deck. Sources: the wiki's Your First Domain and Mailbox, DNS for Your Domain and Ports Reference; SampleDomain.md and SetupChecklist.md; and chapters 6, 8 and 22 of the public documentation.