-
Notifications
You must be signed in to change notification settings - Fork 3
Changes Since 6210
Every user-visible change from 6.2.11 to 6.2.28 by release and by area, the schema changes, the upgrading notes, what changes on upgrade without a switch, what was removed, the new INI keys with their defaults, and (section 4a) what 6.2.28 ships that no earlier release had. File and line references point into the repository at the commit this page was written from (6.2.24, master of 4 September 2026); the 6.2.25, 6.2.26, 6.2.27 and 6.2.28 material was checked against master 40ae9491d on 8 September 2026 and its line numbers are from that tree, except for the last commits before the tag, which are cited from the tree v6.2.28 was itself cut from - there the HttpProxy line at IniFileSettings.cpp:492 moves everything below it down by one. Sections headed Unconfirmed or Contradictions record what could not be verified or where documents disagreed, and are left in on purpose.
Built 2026-09-04 from the GitHub release bodies (gh release view <tag>) for every release after v6.2.10, Roadmap.md (the ✅ rows carrying a 2026 date), and the code at commit 4d5da3f (branch server-fixes-wave, tree identical to v6.2.24 for the files cited). Every setting name, default, port, keyword and command below was grepped in the tree; the (source: …) gives the file:line. Anything that could not be located in the code is under Unconfirmed at the end, not stated as fact.
Conventions: IniFileSettings.cpp = hmailserver/source/Server/Common/Application/IniFileSettings.cpp. IDL = hmailserver/source/Server/hMailServer/hMailServer.idl. DBScripts = hmailserver/source/DBScripts. "CP" = the WPF Control Panel at hmailserver/source/Tools/ControlPanel. "In Ctrl+K index" means the key appears in hmailserver/source/Tools/ControlPanel/Services/SettingsSearchIndex.g.cs.
| Tag | Published (UTC) | Kind | Schema after install | Regression gate | Headline |
|---|---|---|---|---|---|
| v6.2.11 | 2026-08-07 | stable | 6005 | 1026/1026 | Control Panel accessibility fix; suite runs in full for the first time (SpamAssassin + ClamAV installed) |
| v6.2.12 | 2026-08-10 | stable | 6005 | 1026/1026 | Setup tools to .NET 8 SDK-style; VB6 migration tools replaced by ImportTool; installer hardening |
| v6.2.13 | 2026-08-11 | stable | 6005 | 1026/1026 | Community bug fixes; SMTP/IMAP/SpamAssassin hardening; Win10 1607 floor |
| v6.2.14 | 2026-08-11 | stable | 6005 | 1026/1026 | 21 audit defects; Ctrl+K settings search; settings moved to the right pages |
| v6.2.15 | 2026-08-11 | stable | 6005 | 1040/1040 | IMAP sequence sets per RFC 3501; orphan-file fixes; restore no longer destroys data dir; four new CP pages |
| v6.2.16 | 2026-08-11 | stable | 6005 | 1040/1040 | One CP fix (#21, Ctrl+K palette error dialog) |
| v6.2.17 | 2026-08-12 | stable | 6005 | (not stated) | Root cause of #18 (relayed mail stalling after 354): FinalizationTimeout, bounded SpamAssassin wait |
| v6.2.18 | 2026-08-12 | stable | 6005 (notes silent; 6.2.19 says "moves from 6005") | 1049 | DB-unavailable RCPT → 451; every pooled wait bounded; first /analyze pass; Roadmap rewritten |
| v6.2.19 | 2026-08-15 | stable, fresh installs broken | 6011 | (not stated) | Custom-DNS regression fix (#25); directory (AD) provisioning; Sieve imap4flags; INI settings into DB |
| v6.2.20 | 2026-08-15 | stable | 6011 | "1,490-test suite" mentioned | Fresh-install fix for 6.2.19; IMAP THREAD; installer never hangs under /SUPPRESSMSGBOXES |
| v6.2.21 | 2026-08-15 | stable (last stable before 6.2.24) | 6011 | (not stated) | Postfix QUIT-behind-dot hang fixed for real; dot transparency; Sieve body; Sieve trees follow renames; SORT hang |
| v6.2.22-pre1 | 2026-08-16 | pre-release | 6014 | "1,600-test" | ACME http-01 fix (#34); log flush (#33); M365 XOAUTH2 both directions; Sieve wave; eleven IMAP extensions |
| v6.2.22-pre2 | 2026-08-18 | pre-release (build 27) | 6015 | 1650/1650 | DMARC rua reports; per-account lockout; POP3 AUTH-RESP-CODE/PIPELINING; AVFailAction; IMAP BINARY, OBJECTID |
| v6.2.22-pre3 | 2026-08-19 | pre-release (build 28) | 6019 | 1700/1700 | App passwords; per-account TOTP; password policy/expiry/history; quarantine; POP3 EXPIRE/LOGIN-DELAY; ES256 |
| v6.2.22-pre4 | (withdrawn) | tag only, no release | 6020 | — | Installer could not create its own DB (SQL CE); tag v6.2.22-pre4 still exists, do not build from it |
| v6.2.22-pre5 | (superseded) | tag only, no release | 6022 | — | Same build as pre6 but published without SBOM/signature assets; re-cut as pre6 |
| v6.2.22-pre6 | 2026-08-20 | pre-release (build 30, "Pre-Alpha") | 6022 | 1721 | DMARCbis tree walk + np=; rsa-sha1 refused; SPF void limit; ACME ARI; RCPT-time over-quota; quota warnings; per-domain relay; message trace; archive retention; per-domain metrics |
| v6.2.23-alpha1 | 2026-08-21 | pre-release (build 32) | 6025 | 1831/1831 | Shared mailboxes; full-text index; RFC 3464 DSNs; BINARYMIME; PROXY/XCLIENT; filter hook; blocked senders; per-account spam; DL moderation; domain OOO; Event Log; OTLP metrics/logs; IPv6 listeners; disk floor; CP overhaul |
| v6.2.23-alpha2 | 2026-09-04 | pre-release | 6025 | 1838/1838 | Thunderbird UTF8 (~{n} APPEND (#53); two anti-spam bypasses ported from upstream; STARTTLS re-EHLO 503; CFG; signed tags |
| v6.2.24 | 2026-09-04 | stable | 6025 | 1838/1838 | Code of alpha2 plus NuGet lock files; first stable since 6.2.21 |
| v6.2.25 | 2026-09-06 | stable | 6030 | 2001 run, 1994 passed, 7 explicit skips (same on the assertion build) | ACME issuance/renewal crash (#93); external accounts over IMAP; message retention; archive index + legal hold; metric history; SASL EXTERNAL; scrypt + work factor; tarpits; server-enforced administrator 2FA; REST reaches COM-only objects; outbound PIPELINING/CHUNKING; seventeen foreign keys; OpenSSL 4.0.2 + Boost 1.92.0; C++ CodeQL runs on a hosted runner |
| v6.2.26 | 2026-09-06 | stable | 6030 | 2004 run, 1997 passed, 7 skips | One fix: SQL Server Compact upgrades through 6030 reported as failed after succeeding, and the probe crashed the provider (#114) |
| v6.2.27 | 2026-09-07 | stable | 6031 | 2024 run, 2017 passed, 7 skips | DNSSEC: a missing DS is proved missing (NSEC/NSEC3); FetchAccount.MirrorFolders; Import Tool reads Maildir; docs/Migration.md |
| v6.2.28 | 2026-09-08 | stable | 6031 (unchanged) | 2127 run, 2119 passed, 8 skips | Live update (check, Sigstore verify, apply with rollback) and HttpProxy; self-service portal /portal and /api/v1/me; REST API and web services on HttpServer; IMAP COMPRESS=DEFLATE; Control Panel in 17 languages; the Control Deck's four read-only views |
| v6.3.0 | 2026-09-10 | stable | 6031 (unchanged) | 2175 run, 2166 passed, 9 skips | Linux on x86-64 and AArch64: .deb, .rpm and AppImage packages, and the regression suite on a Linux server (756 passed, 649 skipped of 1,405); the /Debug console server; the webmail's first waves |
| v6.3.1 | 2026-09-11 | stable | 6031 (unchanged) | 2175 run, 2166 passed, 9 skips | The installer is Authenticode-signed (Progressive Robot Ltd, countersigned); nine release-path fixes found by executing the release workflows against the live asset list |
| v6.3.2 | 2026-09-13 | stable | 6038 | 2,219 tests, 2,211 passed, 0 failed, 8 skipped | The webmail's fifteen waves (address book, identities and signature, Archive/Junk/Trash, search, labels, Security/Away/Storage/Files pages, templates, receipts, one-click unsubscribe, send later and snooze, the editor and the app install, branding, files as links, twenty languages, S/MIME); SASL GSSAPI on Windows; an auto-ban that reaches the firewall; a SURBL expected result; static Boost on Linux and a container image; thirty-seven binaries out of git; the 6029-6030 sweep fix with an upgrade gate; the MySQL create-script fix; the refused-database COM guard |
| v6.3.3 | 2026-09-15 | stable, latest | 6040 | 2,302 tests, 2,294 passed, 0 failed, 8 skipped | The webmail rebuilt to the shape of Gmail and Outlook.com (reading pane, tabs, mute, pin, block, sweep, drag-and-drop, docked compose, follow-up flags, nudges, quick steps, eighteen more search operators); CardDAV; the Linux regression suite's whole route backlog and the browser Control Deck at parity with the Control Panel; an outbound BDAT chunk larger than one buffer no longer stalls (#261); an upgrade from a schema older than 6038 works again (#263); PostgreSQL stores a backslash as one; the live update from an open Control Panel; the REST ini routes answer to the administrator password only |
- There is no stable 6.2.22 and no stable 6.2.23; both were pre-release-only lines (source:
gh release list). - Only pre1, pre2, pre3 and pre6 of the 6.2.22 line have releases; pre4 and pre5 exist only as tags (source:
gh release list;git tag -l "v6.2.2*"). - Current tree:
HMAILSERVER_VERSION "6.2.28", build 38 (source: hmailserver/source/Server/Common/Application/Version.h:5-7); the installer ishMailServer-6.2.28-x64.exe(source: installation/section_setup_64.iss:2). -
REQUIRED_DB_VERSION 6040since 6.3.3 (source: hmailserver/source/Server/Common/Application/Constants.h); fresh installs are stamped 6040 (source: DBScripts/CreateTablesMSSQL.sql); 6.2.28, 6.3.0 and 6.3.1 were at 6031 and 6.3.2 at 6038. Everything after v6.2.27 (47 commits,git log v6.2.27..v6.2.28) is what v6.2.28 ships — see section 4a. - Published (UTC): v6.2.25 2026-09-06T10:00Z, v6.2.26 2026-09-06T19:49Z, v6.2.27 2026-09-07T01:12Z, v6.2.28 2026-09-08 (source:
gh release view). - Tag dates (local, +0100) for placing dated Roadmap rows: v6.2.18 2026-08-12 06:24; v6.2.19 2026-08-15 08:46; v6.2.20 2026-08-15 14:10; v6.2.21 2026-08-16 01:27; pre1 2026-08-16 23:33; pre2 2026-08-18 02:48; pre3 2026-08-20 00:43; pre6 2026-08-21 00:02; alpha1 2026-08-21 20:26; alpha2 2026-09-04 18:44 (source:
git for-each-ref refs/tags).
Read left to right. The number under each release is the schema it leaves the database on; a release with no number changed nothing. Everything in a grey box shipped in one day — this project's cadence is deliberately many small releases rather than few large ones, and the reason is Release Process's first standing rule.
flowchart LR
subgraph aug1["7-12 August 2026 - schema 6005 throughout"]
direction LR
V11["6.2.11"] --> V12["6.2.12"] --> V13["6.2.13"] --> V14["6.2.14"] --> V15["6.2.15"] --> V16["6.2.16"] --> V17["6.2.17"] --> V18["6.2.18"]
end
subgraph aug2["15-16 August 2026"]
direction LR
V19["6.2.19<br/>6011<br/>fresh installs broken"] --> V20["6.2.20<br/>6011<br/>fresh-install fix"] --> V21["6.2.21<br/>6011"]
end
subgraph aug3["16-21 August 2026 - the 6.2.22 pre-release line"]
direction LR
P1["pre1<br/>6014"] --> P2["pre2<br/>6015"] --> P3["pre3<br/>6019"] --> P4["pre4<br/>6020<br/>WITHDRAWN"] --> P5["pre5<br/>6022<br/>superseded"] --> P6["pre6<br/>6022"]
end
subgraph aug4["21 August - 4 September 2026"]
direction LR
A1["6.2.23-alpha1<br/>6025"] --> A2["6.2.23-alpha2<br/>6025"] --> V24["6.2.24<br/>6025<br/>first stable since 6.2.21"]
end
subgraph sep["6-8 September 2026"]
direction LR
V25["6.2.25<br/>6030"] --> V26["6.2.26<br/>6030<br/>one fix"] --> V27["6.2.27<br/>6031"] --> V28["6.2.28<br/>6031"]
end
subgraph sep2["10-15 September 2026"]
direction LR
V30["6.3.0<br/>6031<br/>Linux"] --> V31["6.3.1<br/>6031<br/>signed"] --> V32["6.3.2<br/>6038"] --> V33["6.3.3<br/>6040<br/>LATEST"]
end
V28 --> V30
V18 --> V19
V21 --> P1
P6 --> A1
V24 --> V25
There is no stable 6.2.22 and no stable 6.2.23 — both were pre-release-only lines.
v6.2.22-pre4 and -pre5 exist as tags with no release behind them; do not build from
either.
The gate is the full suite run on the exact binary being shipped, and its size is the single best proxy for how much of this server is now covered by a test that talks to it over a real socket.
| Release | Tests run | Result as published |
|---|---|---|
| 6.2.11 – 6.2.14 | 1,026 | all passing |
| 6.2.15 – 6.2.16 | 1,040 | all passing |
| 6.2.18 | 1,049 | all passing |
| 6.2.20 | ~1,490 | "1,490-test suite" |
| 6.2.22-pre2 | 1,650 | 1650/1650 |
| 6.2.22-pre3 | 1,700 | 1700/1700 |
| 6.2.22-pre6 | 1,721 | as published |
| 6.2.23-alpha1 | 1,831 | 1831/1831 |
| 6.2.23-alpha2 / 6.2.24 | 1,838 | 1838/1838 |
| 6.2.25 | 2,001 | 1,994 passed, 7 explicit skips — same on the assertion build |
| 6.2.26 | 2,004 | 1,997 passed, 7 skips |
| 6.2.27 | 2,024 | 2,017 passed, 7 skips |
| 6.2.28 | 2,127 | 2,119 passed, 0 failed, 8 skips |
The eighth skip at 6.2.28 is one ignored test where STARTTLS is not offered on the plain IMAP port of this particular bench; the other seven are the explicit stress skips.
Schema was 6005 from 6.2.10 through 6.2.18 (source: 6.2.11–6.2.17 release notes each say "schema version 6005"). All steps below have MSSQL, MSSQLCE, MySQL and PGSQL variants in DBScripts/Upgrade<from>to<to><backend>.sql.
| Step | First shipped in | What it does (MSSQL script content) |
|---|---|---|
| 6005→6006 | 6.2.19 |
hm_imapfolders.folderspecialuse (source: DBScripts/Upgrade6005to6006MSSQL.sql) |
| 6006→6007 | 6.2.19 |
hm_domains.domaindkimsecondaryselector, domaindkimsecondaryprivatekeyfile (DKIM key rotation) (source: Upgrade6006to6007MSSQL.sql) |
| 6007→6008 | 6.2.19 |
hm_tcpipports.portclientcertificatepolicy, portclientcertificatecafile (source: Upgrade6007to6008MSSQL.sql) |
| 6008→6009 | 6.2.19 |
hm_sslcertificates.sslprivatekeypassword (source: Upgrade6008to6009MSSQL.sql) |
| 6009→6010 | 6.2.19 |
hm_settings rows ASArcFilteringEnabled, ASArcTrustedSealers (source: Upgrade6009to6010MSSQL.sql) |
| 6010→6011 | 6.2.19 | new table hm_inisettings (INI [Settings] mirrored into the DB) (source: Upgrade6010to6011MSSQL.sql) |
| 6011→6012 | 6.2.22-pre1 |
hm_rule_criterias.criteriamatchvalue widened to nvarchar(2000); hm_accounts.accountvacationbegindate (source: Upgrade6011to6012MSSQL.sql) |
| 6012→6013 | pre1 |
hm_messages.messagesavedate (IMAP SAVEDATE) (source: Upgrade6012to6013MSSQL.sql) |
| 6013→6014 | pre1 | new table hm_imap_metadata (IMAP METADATA) (source: Upgrade6013to6014MSSQL.sql) |
| 6014→6015 | pre2 |
hm_messages.messageemailid (IMAP OBJECTID EMAILID) (source: Upgrade6014to6015MSSQL.sql) |
| 6015→6016 | pre3 | new table hm_apppasswords (source: Upgrade6015to6016MSSQL.sql) |
| 6016→6017 | pre3 |
hm_accounts.accounttotpsecret (per-account TOTP) (source: Upgrade6016to6017MSSQL.sql) |
| 6017→6018 | pre3 | new table hm_quarantine (source: Upgrade6017to6018MSSQL.sql) |
| 6018→6019 | pre3 |
hm_accounts.accountpasswordchanged; new table hm_passwordhistory (source: Upgrade6018to6019MSSQL.sql) |
| 6019→6020 | pre4 (withdrawn) / pre6 | new table hm_messagetrace (source: Upgrade6019to6020MSSQL.sql) |
| 6020→6021 | pre6 | six hm_domains.domainrelay* columns (per-domain outbound relay) (source: Upgrade6020to6021MSSQL.sql) |
| 6021→6022 | pre6 |
hm_servermessages rows QUOTA_WARNING, QUOTA_WARNING_SUBJECT (source: Upgrade6021to6022MSSQL.sql) |
| 6022→6023 | 6.2.23-alpha1 | new tables hm_messageindexterms, hm_messageindexstate (full-text index; empty until enabled) (source: Upgrade6022to6023MSSQL.sql) |
| 6023→6024 | alpha1 | six hm_domains.domainvacation* columns; new table hm_blocked_senders (source: Upgrade6023to6024MSSQL.sql) |
| 6024→6025 | alpha1 |
hm_messages.messageflags tinyint → smallint (table rewrite on MSSQL/SQLCE/MySQL; PostgreSQL already smallint); hm_accounts.accountantispamenabled (default 1), accountspammarkthreshold (-1), accountspamdeletethreshold (-1); hm_distributionlists.distributionlistmoderatoraddress, distributionlistbounceaddress (source: Upgrade6024to6025MSSQL.sql) |
| 6025→6026 | 6.2.25 |
hm_settings row CreateDefaultSpecialUseFolders (0) (source: Upgrade6025to6026MSSQL.sql) |
| 6026→6027 | 6.2.25 |
hm_domains.domainmessageretentiondays, hm_accounts.accountmessageretentiondays, both default 0 = no policy (source: Upgrade6026to6027MSSQL.sql) |
| 6027→6028 | 6.2.25 | new table hm_metricsamples (one row per metric per minute; empty until the sampler runs) (source: Upgrade6027to6028MSSQL.sql) |
| 6028→6029 | 6.2.25 | new table hm_archiveindex (only copies made from now on are indexed) (source: Upgrade6028to6029MSSQL.sql) |
| 6029→6030 | 6.2.25 |
seventeen FOREIGN KEY ... ON DELETE CASCADE constraints (accounts/aliases/domain aliases/lists → domain, list recipients → list, route addresses → route, fetch accounts/app passwords/password history/index state → account, fetch UIDs → fetch account, rule criteria/actions → rule, group members → group, message recipients/metadata/index terms → message, expunged marks → folder); orphan rows deleted first, children before parents; MySQL gets ENGINE=InnoDB on every table involved. Reads every child table once — plan for it like an index build (source: Upgrade6029to6030MSSQL.sql; v6.2.25 body) |
| 6030→6031 | 6.2.27 |
hm_fetchaccounts.famirrorfolders tinyint default 0 (source: Upgrade6030to6031MSSQL.sql) |
| 6031→6032 | 6.3.2 | new table hm_contacts (the webmail's address book, one row per account and address, unique on both) (source: Upgrade6031to6032MSSQL.sql) |
| 6032→6033 | 6.3.2 | new table hm_accountprefs (the account's own preferences: theme, density, notifications, language, templates, label colours) (source: Upgrade6032to6033MSSQL.sql) |
| 6033→6034 | 6.3.2 | new table hm_scheduled (a draft to send at a time, a message snoozed until a time) (source: Upgrade6033to6034MSSQL.sql) |
| 6034→6035 | 6.3.2 | new table hm_files (files sent as links: the chunked store, its tokens, passwords and expiry) (source: Upgrade6034to6035MSSQL.sql) |
| 6035→6036 | 6.3.2 |
hm_messages.messagekeywords nvarchar(500) default '' (IMAP keywords, the webmail's labels) (source: Upgrade6035to6036MSSQL.sql) |
| 6036→6037 | 6.3.2 | new table hm_smimekeys (the account's own certificates with their chains and private keys wrapped in the browser, and the correspondents' certificates) (source: Upgrade6036to6037MSSQL.sql) |
| 6037→6038 | 6.3.2 |
hm_surblservers.surblresult nvarchar(255) default '' (a SURBL server's expected result) (source: Upgrade6037to6038MSSQL.sql) |
| 6038→6039 | 6.3.3 |
hm_domains.domainrelaypassword nvarchar(1024) (a DPAPI envelope is 314 characters; the column was 255, so a domain's relay password could not be saved) (source: Upgrade6038to6039MSSQL.sql) |
| 6039→6040 | 6.3.3 |
hm_contacts.contacturi, contactuid nvarchar(255) default '' and contactvcard ntext default '' (the card itself, as a CardDAV client sent it; mediumtext on MySQL) (source: Upgrade6039to6040MSSQL.sql) |
- The 6.2.24 notes: "from 6.2.21 the schema moves from 6011 to 6025 in fourteen steps, one way" (source: v6.2.24 release body, "Upgrading from 6.2.21").
- Note: the pre1 body says "schema 6012 → 6014"; 6.2.21 was at 6011, so the real move for a pre1 upgrader was 6011 → 6014 (source: pre1 body vs 6.2.24 body).
- DBUpdater registers the steps through
new UpgradeScript(6030, 6031)and, since 6.3.2,new UpgradeScript(6031, 6032)to(6037, 6038), and since 6.3.3(6038, 6039)and(6039, 6040)(source: Tools/DBUpdater/formMain.cs), each with a probe in SchemaVerification.cs that the Compact Edition check and the Linux upgrade gate execute. - 6.2.26 changed no schema; it fixed the SQL Server Compact verification of the 6030 step (#114): the four foreign-key probes used
case when exists (...), which faults the CE provider (the server reportedHM10045, the crash oracle then stopped the service); they now readupdate hm_dbversion set value = value / (value - value) where not exists (select 1 from information_schema.table_constraints ...), andbuild/check-db-scripts.ps1executes every probe against a freshly created database with a negative control (source: v6.2.26 body; build/check-db-scripts.ps1:126-156).
Since 6031, seven steps (all in 6.3.2): the tables hm_contacts, hm_accountprefs, hm_scheduled, hm_files and hm_smimekeys, each with a foreign key to hm_accounts and cascading deletes, hm_messages.messagekeywords, and hm_surblservers.surblresult; then two in 6.3.3: hm_domains.domainrelaypassword widened to 1024 characters, and hm_contacts given contacturi, contactuid and contactvcard for CardDAV. What follows describes 6031 and stays true for everything it names.
Forty-six hm_* tables (grep -i "^create table" DBScripts/CreateTablesMSSQL.sql).
Seventeen of them are the child side of a foreign key, all added at 6029→6030, and that
is what makes deleting a domain or an account clean up after itself. Those seventeen
relationships are drawn below; the twenty-nine tables that are nobody's child are listed
underneath.
erDiagram
hm_domains ||--o{ hm_accounts : "accountdomainid"
hm_domains ||--o{ hm_aliases : "aliasdomainid"
hm_domains ||--o{ hm_domain_aliases : "dadomainid"
hm_domains ||--o{ hm_distributionlists : "distributionlistdomainid"
hm_distributionlists ||--o{ hm_distributionlistsrecipients : "distributionlistrecipientlistid"
hm_routes ||--o{ hm_routeaddresses : "routeaddressrouteid"
hm_accounts ||--o{ hm_fetchaccounts : "faaccountid"
hm_fetchaccounts ||--o{ hm_fetchaccounts_uids : "uidfaid"
hm_accounts ||--o{ hm_apppasswords : "apaccountid"
hm_accounts ||--o{ hm_passwordhistory : "phaccountid"
hm_rules ||--o{ hm_rule_criterias : "criteriaruleid"
hm_rules ||--o{ hm_rule_actions : "actionruleid"
hm_groups ||--o{ hm_group_members : "membergroupid"
hm_messages ||--o{ hm_messagerecipients : "recipientmessageid"
hm_messages ||--o{ hm_message_metadata : "metadata_messageid"
hm_messages ||--o{ hm_messageindexterms : "mitmessageid"
hm_imapfolders ||--o{ hm_imapexpunged : "expungedfolderid"
Every one of those is ON DELETE CASCADE. That is the point: before 6030, deleting a
domain left its accounts' app passwords, password history, fetch-account UID lists and
rule criteria behind as orphan rows, and nothing ever collected them.
The twenty-nine tables that are not the child of any foreign key, and what each is for. Five of them are parents in the diagram above; the rest stand alone:
| Table | Holds |
|---|---|
hm_domains |
The domains this server is authoritative for - the root of most of the cascade above |
hm_dbversion |
The single row the server compares against REQUIRED_DB_VERSION. A mismatch is error 5011 |
hm_settings |
Server settings the Control Panel and COM API edit |
hm_inisettings |
The [Settings] INI keys mirrored into the database (added at 6011) |
hm_tcpipports |
The listening ports. A fresh install seeds exactly four: 25, 587, 110, 143 |
hm_sslcertificates |
Certificates available to bind to a TLS port |
hm_securityranges |
IP ranges: who may connect, authenticate and relay |
hm_imapfolders |
The folder hierarchy; message bodies are on disk, not here |
hm_messages |
One row per queued or delivered message |
hm_imap_metadata |
RFC 5464 METADATA entries (added at 6014) |
hm_messageindexstate |
Full-text indexing progress (added at 6023; empty until enabled) |
hm_messagetrace |
Per-message trace records (added at 6020; empty until MessageTraceEnabled) |
hm_metricsamples |
One row per metric per minute (added at 6028) |
hm_archiveindex |
The archive's searchable index (added at 6029; only copies made from then on) |
hm_quarantine |
Held suspected spam (added at 6018) |
hm_greylisting_triplets |
The greylisting state machine's memory |
hm_greylisting_whiteaddresses |
Senders exempt from greylisting only |
hm_whitelist |
Senders exempt from spam checking |
hm_blocked_senders |
Claimed sender addresses and domains refused with a score (added at 6024) |
hm_blocked_attachments |
Attachment-name wildcards to strip |
hm_dnsbl |
Configured DNS blacklists |
hm_surblservers |
Configured URL blocklists |
hm_incoming_relays |
Upstream gateways whose IP is not the real client |
hm_logon_failures |
Failed-logon counters behind auto-ban and lockout |
hm_acl |
Public-folder permissions |
hm_groups |
Account groups (the parent side of hm_group_members) |
hm_routes |
Per-domain delivery overrides (the parent side of hm_routeaddresses) |
hm_rules |
Global and account rules (the parent side of criteria and actions) |
hm_servermessages |
The text of bounces and system messages |
flowchart LR
S6005["6005<br/>6.2.10 - 6.2.18"] --> S6011["6011<br/>6.2.19 - 6.2.21<br/>six steps"]
S6011 --> S6022["6022<br/>the 6.2.22 pre-releases<br/>eleven steps"]
S6022 --> S6025["6025<br/>alpha1, alpha2, 6.2.24<br/>three steps"]
S6025 --> S6030["6030<br/>6.2.25 and 6.2.26<br/>five steps, incl. the<br/>seventeen foreign keys"]
S6030 --> S6031["6031<br/>6.2.27 and 6.2.28<br/>one step"]
The chain is one way. The server refuses to start on a database older or newer than the binary requires (error 5011; 5010 when the version cannot be read), so downgrading a server without restoring the matching database backup does not work — see Upgrading Guide and Backup and Restore.
Two upgrade steps deserve planning rather than a click:
-
6024→6025 rewrites
hm_messageson MS SQL Server, SQL CE and MySQL, becausemessageflagswidens fromtinyinttosmallint. PostgreSQL was alreadysmallintand is untouched. - 6029→6030 adds the seventeen foreign keys, which means deleting orphan rows first and then reading every child table once. Plan for it like an index build.
- 6.2.11: "Drop-in over 6.2.10. No database change (schema version 6005), no configuration change, no server-core change." (source: v6.2.11 body)
- 6.2.12: "No server-core changes; no database change (schema version 6005)." .NET 8 Desktop Runtime becomes a server-component prerequisite; supported-OS floor Windows 10 1607 (source: v6.2.12 body)
- 6.2.13 / 6.2.14: "in-place over any earlier hMailServer release; the database upgrades automatically (schema version 6005). Minimum OS is Windows 10 1607 / Server 2016, 64-bit." (source: v6.2.13, v6.2.14 bodies)
- 6.2.15–6.2.17: no database change (6005) (source: bodies)
- 6.2.19: "The database schema moves from 6005 to 6011. DBUpdater applies it; take a backup first." Fresh installs of 6.2.19 fail (SQL CE create script batching + silent-install hang) — use 6.2.20 (source: v6.2.19 body warning block)
- 6.2.20: fixes fresh install; "upgrades from existing installations were never affected" (source: v6.2.20 body)
- pre1: "Databases upgrade automatically (schema 6012 → 6014) via the bundled DBUpdater. Take a backup before installing a pre-release." (source: pre1 body)
- pre2: "Upgrading from pre1 requires DBUpdater — schema moves to 6015" (source: pre2 body)
- pre3: "schema moves from 6015 to 6019 in four steps" (source: pre3 body)
- pre6: "schema moves from 6019 to 6022 in three steps" (source: pre6 body)
- alpha1: "6022 to 6025 in three steps; from 6.2.21 (6011) it moves in fourteen"; the upgrade is one-way — an older server refuses a newer
hm_dbversion("The database is too new for this version of hMailServer. Please upgrade hMailServer.") (source: alpha1 body; hmailserver/docs/Upgrading.md:141-143) - alpha1 note 15: coming from 6.2.18 or earlier, the .NET runtime is load-bearing on upgrade — DBUpdater is a .NET app and the server refuses to start on an old schema; if the runtime install fails, install the .NET 10 Desktop Runtime by hand then run
Bin\DBUpdater.exe(source: alpha1 body) - alpha1 note 13: new COM interfaces → the installer runs
hMailServer.exe /RegisterTypeLib; a hand-copied install must re-register elevated (HKLMInterfacekeys), symptom otherwiseREGDB_E_IIDNOTREG(source: alpha1 body; hmailserver/installation/hMailServerInnoExtension.iss:724-744) - 6.2.24 "Behaviour that changes on upgrade, without a switch" (seven items) — reproduced in §5 below (source: v6.2.24 body)
- 6.2.24: early-bound COM clients compiled against a 6.2.22 pre-release interop must be recompiled; late-bound scripts and anything built against 6.2.21 are unaffected (source: v6.2.24 body; alpha1 note 4)
- 6.2.25: "The database schema moves from 6025 to 6030 in five steps, one way"; the 6029→6030 step reads every child table; eight behaviour changes without a switch (see §5); the type library gained members (
Utilities.SearchArchive,SetArchiveHold,GetMetricHistory,SampleMetricsNow,RunMessageRetention;Settings.EnrolAdministratorTOTP/DisableAdministratorTOTP;Application.AdministratorTOTPEnabled;COMAuthentication.AuthenticateWithCode;Domain/Account.MessageRetentionDays;Diagnostics.AssertionsEnabled) — clients compiled against 6.2.24 keep working, recompile to reach them;FetchAccount.ServerType1 now means IMAP (source: v6.2.25 body; hMailServer.idl:810-811, 854, 988, 1239-1243, 1720-1721, 1854, 3221) - 6.2.26: server unchanged; if the 6.2.25 installer reported the SQL Server Compact upgrade as failed, the database is at 6030 with its foreign keys and this installer finds nothing left to upgrade (source: v6.2.26 body)
- 6.2.27: "Schema 6030 → 6031: one column on
hm_fetchaccounts"; from 6.2.25 or 6.2.26 run the installer (source: v6.2.27 body) - 6.2.28: no schema step —
REQUIRED_DB_VERSIONstays 6031, so an upgrade from 6.2.27 is the installer and nothing else; from 6.2.25 or 6.2.26 the 6030 → 6031 step still runs on the way (source: Constants.h:173; Version.h:5-7) - Installer minimum OS:
MinVersion=10.0.14393(Windows 10 1607 / Server 2016) (source: hmailserver/installation/section_setup.iss:24-28; section_messages.iss:3) - Installer bundles the .NET 10 Desktop Runtime (
DOTNET_MAJOR "10") (source: hmailserver/installation/hMailServer64.iss:16-18); all tool projects targetnet10.0-windows(source: hmailserver/source/Tools/ControlPanel/.csproj, DBUpdater/.csproj<TargetFramework>) - .NET 10 migration commit
5409ae8d5is dated 2026-08-12 10:27 (after the v6.2.18 tag, before v6.2.19) → first shipped in 6.2.19, though no 6.2.19 note says so (source:git log; Roadmap.md:184 "done, 12 Aug 2026") - Silent install:
/VERYSILENT /SUPPRESSMSGBOXES— every installer dialog now takes its default (6.2.20); a failed DB step exits non-zero (alpha2); pass the admin password with/adminpassword=(min 5 chars) (source: v6.2.20, alpha2 bodies; hMailServerInnoExtension.iss:64-67, 649-653)
-
EHLO keywords now advertised:
SIZE,8BITMIME,PIPELINING,CHUNKING,BINARYMIME,SMTPUTF8,ENHANCEDSTATUSCODES,DSN,XCLIENT ADDR NAME PORT PROTO HELO LOGIN(trusted peers only),STARTTLS,AUTH LOGIN [PLAIN] SCRAM-SHA-256 [SCRAM-SHA-256-PLUS] [XOAUTH2 OAUTHBEARER],HELP(source: hmailserver/source/Server/SMTP/SMTPConnection.cpp:2447-2556) - 6.2.13: PTR lookup for the
Receivedheader moved off the I/O thread (fix for relayed-mail DATA stall);TCP_NODELAYon every connection; rejectedBDATdrains its payload;EHLO SIZEno longer overflows (source: v6.2.13 body) - 6.2.15: BDAT exact-length reads take only what arrived (a vanished sender no longer yields a NUL-padded delivered message) (source: v6.2.15 body)
- 6.2.17:
FinalizationTimeout= 240 s,0disables — acceptance after end-of-data is bounded; server answers451 4.3.1(source: IniFileSettings.cpp:281; v6.2.17 body). SpamAssassin wait hard ceiling =SAMaxTimeout(default 90) + 30 s (source: IniFileSettings.cpp:275; v6.2.17 body) - 6.2.17: per-stage acceptance timings logged at APPLICATION level ("SMTPConnection - accept: done spam-protection in N ms") (source: v6.2.17 body)
- 6.2.18: a recipient lookup that fails because the database is unavailable answers
451, not550(thread-local marker);DBConnectionAcquireTimeoutdefault 60 (was 0 in 6.2.17) (source: IniFileSettings.cpp:306; v6.2.18 body) - 6.2.21: end-of-data
<CRLF>.<CRLF>is found anywhere in the received buffer and what follows is handed to the command parser (Postfix sends.\r\nQUIT\r\nin one segment) — the real fix for #18; bare-LF terminators still only matched at buffer end and only with "Allow incorrect line endings" (CVE-2023-51764 rule) (source: v6.2.21 body) - 6.2.21: dot-transparency (RFC 5321 4.5.2) carried across buffer boundaries in both directions (source: v6.2.21 body)
- alpha1: BINARYMIME (RFC 3030) advertised;
DATAunderBODY=BINARYMIMErefused503 5.5.1; binary mark persisted as flag bit 256 (why 6025 widenedmessageflags); relay of a binary message refused at RCPT with554 5.6.3(source: SMTPConnection.cpp:2475-2481, 3123; alpha1 body) - alpha1: PROXY protocol v1/v2 —
SMTPProxyProtocolEnabled=0,SMTPProxyProtocolTrustedIPs="" ; XCLIENT —SMTPXClientEnabled=0,SMTPXClientTrustedIPs="" ; trust decided against the real TCP peer; a listed proxy must send the header; unparseable list entry matches nothing (HM6281) (source: IniFileSettings.cpp:620-623; SMTPConnection.cpp:2494-2499, 2962-2974; alpha1 body). Both in CP (Views/FeatureSettingsView.xaml.cs) and README. - alpha1: disk-space floor —
MinimumFreeDiskSpaceMB=100 →452 4.3.1at MAIL FROM,NO [UNAVAILABLE]at IMAP APPEND, POP3 fetcher pauses;DiskSpaceWarningThresholdMB=1024 logs one line;0disables (source: IniFileSettings.cpp:417-420; alpha1 note 5) - alpha2:
RSETbeforeEHLO, orSTARTTLSwithout a freshEHLO, no longer opens a transaction —MAIL FROM/AUTHafter the TLS handshake without EHLO gets503 Bad sequence of commands(RFC 3207 §4.2) (source: SMTPConnection.cpp:527, 554; alpha2 body) - alpha2: Received-header parsing for relayed/fetched mail takes the last observed address before
by, ignores HELO-supplied values, keeps headers with non-domain host names — verdicts move toward the address that actually connected (source: alpha2 body; parser in hmailserver/source/Server/Common/Util/Utilities.cpp) - Rate limit knob (pre-existing, findable since 6.2.14):
MaxSubmissionsPerIPPerMinute=0 (off) (source: IniFileSettings.cpp:618)
- 6.2.14: multiple smart hosts —
|-separated hosts in the relayer field fail over ("always worked; nothing said so") (source: v6.2.14 body) - 6.2.14: MTA-STS enforcement / MX failover state no longer leaks between recipient batches (source: v6.2.14 body)
- 6.2.15: an unreadable message file fails the delivery attempt immediately (connection dropped, not
QUIT); only a genuinely missing file fails permanently (source: v6.2.15 body) - 6.2.18:
ClientSessionCeiling= 1800 s absolute ceiling per outbound session, separate from idle timeout; ClamAV on the delivery path bounded;DNSQueryTimeout=10,ScriptTimeout=60,ExternalProcessTimeout=300 (source: IniFileSettings.cpp:293-308; v6.2.18 body) - pre1: EHLO is the opener on every outbound delivery (previously only on routes needing TLS/auth), HELO fallback kept; outbound
SIZE(RFC 1870) declared on MAIL FROM and an oversize message refused locally before upload (source: hmailserver/source/Server/SMTP/SMTPClientConnection.cpp:588; pre1 body) - pre1: outbound XOAUTH2 for routes whose host is in
OutboundOAuth2Hosts(defaultsmtp.office365.com); client-credentials token fromOutboundOAuth2TokenUrl/ClientId/ClientSecret/Scope(defaulthttps://outlook.office365.com/.default),OutboundOAuth2FixedTokenfor a pre-issued token; token cached to 80 % lifetime, dropped on refusal (source: IniFileSettings.cpp:210-216; SMTPClientConnection.cpp:459-472; pre1 body). Settings in CPViews/ServerSettingsView.xaml.cs; not in README. - pre6: per-domain outbound relay (schema 6021):
Domain.RelayHostetc. (IDL id 44); matched by sending domain; order is route → sending domain's relay → server-wide relayer; empty host = no opinion (source: IDL:884-885; DBScripts/Upgrade6020to6021MSSQL.sql; pre6 body) - pre6: full mailbox refused at RCPT with
452 4.2.2—RejectFullMailboxAtRcpt=1 (set 0 for old accept-then-bounce) (source: IniFileSettings.cpp:641; pre6 body) - alpha1: RFC 3464 DSNs — every bounce is
multipart/report; report-type=delivery-status; codes: full mailbox5.2.2, DB write failure4.3.0, disk floor4.3.1, Sieve reject5.7.1;Arrival-Date/Original-Recipientomitted (source: alpha1 body) - alpha1: local delivery could silently lose a message when the account-level copy failed to write; the sender is now told (source: alpha1 body "Fixes")
- Known, unchanged: equal-preference MX records are not randomised (source: v6.2.21, pre1 bodies).
- 6.2.25: outbound PIPELINING and CHUNKING — the envelope is pipelined and the message sent as one BDAT chunk when the remote advertises both;
OutboundPipelining=1,OutboundChunking=1, each used only when the remote advertises the extension; a binary message is now relayed as-is to a remote that accepts BINARYMIME (the local-only rule of alpha1 still applies to remotes that do not) (source: IniFileSettings.cpp:732-733; v6.2.25 body) - Pre-existing knob surfaced in 6.2.14:
MaxOutboundPerDestinationPerMinute=0 (source: IniFileSettings.cpp:673)
-
Full CAPABILITY line today:
IMAP4 IMAP4rev1 IMAP4rev2 CHILDREN+IDLE(if enabled) +QUOTA QUOTA=RES-STORAGE(if enabled) +SORT(if enabled) +THREAD=ORDEREDSUBJECT THREAD=REFERENCES(unconditional) +ACL(if enabled) +STARTTLS+LOGINDISABLED(when auth refused on cleartext) +AUTH=PLAIN AUTH=SCRAM-SHA-256 [AUTH=SCRAM-SHA-256-PLUS](only ifEnableImapSASLPlain) +AUTH=XOAUTH2 AUTH=OAUTHBEARER(ifOAuth2Enabled) +SASL-IR+NAMESPACE RIGHTS=texk MOVE ID SPECIAL-USE CREATE-SPECIAL-USE UNSELECT UIDPLUS ENABLE STATUS=SIZE ESEARCH CONDSTORE QRESYNC LIST-EXTENDED SEARCHRES WITHIN UTF8=ACCEPT+LIST-STATUS PREVIEW MULTIAPPEND REPLACE SAVEDATE METADATA OBJECTID BINARY+UNAUTHENTICATE(authenticated state only) +LITERAL-+APPENDLIMIT/APPENDLIMIT=<bytes>(authenticated) (source: hmailserver/source/Server/IMAP/IMAPCommandCapability.cpp:24-190) -
Inert by default:
EnableImapSASLPlainships0in the DB and gates the wholeAUTHENTICATEcommand, soAUTH=SCRAM-SHA-256is unreachable over IMAP on a stock install (source: DBScripts/CreateTablesMSSQL.sql:1037; IMAPCommandCapability.cpp:80-95; alpha1 "Known limitations" 22) - 6.2.13: partial-fetch range crash/OOB read fixed;
OnClientLogonfires from everyAUTHENTICATEmechanism (source: v6.2.13 body) - 6.2.14: APPEND reports failure on disk-full; CLOSE notifies other sessions; RENAME cannot make a folder its own parent;
[UNSEEN]is a sequence number;\Recentset maintained;BODY.PEEKno longer cancels\Seenfrom another item; IMAP SASL credentials masked in logs (source: v6.2.14 body) - 6.2.15: sequence sets:
*resolves on either side of:, descending ranges normalised, in all four parsers (FETCH/STORE/COPY/MOVE, SEARCH, UID EXPUNGE, QRESYNC VANISHED) —UID STORE *:*no longer flags every message (source: v6.2.15 body);STATUS (RECENT)per folder (source: v6.2.15 body) - 6.2.18: pre-authentication command buffering capped at 11 MB (source: v6.2.18 body; IMAPConnection.cpp:238
iMaxCommandBuffer = 11 * 1024 * 1024) - 6.2.19:
GetUniqueMessageIDduplicate-UID fix (source: v6.2.19 body);LOGINDISABLEDadvertised (Roadmap "shipped 13 August 2026" → 6.2.19) (source: Roadmap.md:500; IMAPCommandCapability.cpp:73-74) - 6.2.20: THREAD (RFC 5256) both algorithms,
UID THREAD(source: IMAPCommandCapability.cpp:44-49; v6.2.20 body). (pre6 body's "correction" that THREAD "has been implemented all along" refers to a stale Roadmap row; it shipped in 6.2.20.) - 6.2.21:
SORT ()/ bareSORT (REVERSE)rejected instead of spinning a core (source: v6.2.21 body); message-list cache eviction fixed (512 MB ceiling now works) (source: v6.2.21 body) - pre1: APPENDLIMIT (RFC 7889,
TOOBIG), LITERAL- (RFC 7888), LIST-STATUS (RFC 5819), UNAUTHENTICATE (RFC 8437, failed-login counter survives), QUOTA=RES-STORAGE/OVERQUOTA/SETQUOTA refused (RFC 9208), PREVIEW (RFC 8970, 256-octet), MULTIAPPEND (RFC 3502, atomic), REPLACE (RFC 8508, stored before delete), SAVEDATE (RFC 8514, schema 6013), METADATA (RFC 5464, schema 6014) (source: IMAPCommandCapability.cpp:127-190; IMAPConnection.cpp:178-181, 772-781; pre1 body) - pre2: BINARY (RFC 3516:
BINARY[],BINARY.PEEK[],BINARY.SIZE[], literal8~{n}on APPEND) — composite sections empty in pre2, fixed pre3; OBJECTID (RFC 8474:EMAILIDsurvives COPY,MAILBOXIDsurvives RENAME,THREADIDNIL; schema 6015) (source: IMAPCommandCapability.cpp:137-146; pre2, pre3 bodies) - alpha1: shared/delegated mailboxes — Other Users namespace
#Users.owner@domain.folder, reachable only where the owner's folder carries an RFC 4314 ACL; no separate switch: it is theenableimapaclsetting (DB default1), and turning ACL off makes public folders open to everyone; no CP surface for grants (SETACL from a client);\Seenis shared state; everyLISTruns one extra query (source: DBScripts/CreateTablesMSSQL.sql:914; alpha1 note 9, limitations 8-10) - alpha1: full-text index —
IndexerFullText=0 and DB settingMessageIndexing(default 0, CP checkbox "Enable message indexing", COMSettings.MessageIndexingIDL id 89) must both be on; index only narrows the scan, results identical on/off; tuningIndexerFullTextBatchSize=250,IndexerFullTextMinTokenLength=3,IndexerFullTextMaxTokensPerMessage=2048; errorsHM6260(source: IniFileSettings.cpp:365-390; CreateTablesMSSQL.sql:1013; IDL:671; alpha1 body) - alpha1: QRESYNC tombstone pruning —
IMAPExpungeRetentionRecords=5000 per mailbox,0keeps all; one bulk delete at first start after upgrade; RFC 7162 §3.2.6 fallback built first;VANISHED (EARLIER)now sent before FETCH responses (source: IniFileSettings.cpp:646; alpha1 note 8, "Protocol correctness") - alpha2:
APPEND … UTF8 (~{n}wrapper (RFC 6855) accepted — Thunderbird 128+ Sent copies were refused withBAD APPEND Command requires at least 2 parameter(#53) (source: alpha2 body) - alpha2: COM
IMAPFolder.Deletereports a refused deletion instead of S_OK (source: alpha2 body) - 6.2.25: every session numbers its selected folder for itself (upstream #602) — another session's expunge no longer renumbers a client's messages; a message found gone answers
NO [EXPUNGEISSUED], QRESYNC sessions hearVANISHED; sessions stop sharing unguarded state (upstream #580, #566: one lock on the connection, serialised lazy collections, snapshots for cross-thread readers); one strand per connection, so a notification written to an idling IMAPS session cannot run inside the SSL engine mid-read; default special-use folders at account creation (Settings.CreateDefaultSpecialUseFoldersEnabled, off; schema 6026); one authorisation choke point — every folder-access decision isACLManager::CheckPermissionorCheckDelegatedRight, delivery and rules included, andbuild/check-authz-choke-point.pyinstyle.ymlfails any other caller (source: v6.2.25 body; DBScripts/Upgrade6025to6026MSSQL.sql; Common/Application/ACLManager.h:49,57; .github/workflows/style.yml:100) - 6.2.25: IMAP SEARCH BODY and TEXT read the text-bearing attachments (
MessageData::GetAttachmentText: parts that are attachments by the MIME layer's rule and whose media type is text, decoded, bounded at 1 MB) and the full-text index tokenises the same text, so an attached .txt/.csv/.html is searchable and the index never hides it (source: Common/BO/MessageData.h:63; v6.2.25 body; IMAP/AttachmentTextSearch.cs) - 6.2.28: COMPRESS=DEFLATE (RFC 4978) — both directions compressed on request, advertised while
IMAPCompressionEnabled=1 and the session is not yet compressed; zlib 1.3.1 vendored underServer/zlib(source: IMAPCommandCapability.cpp:61-62; IniFileSettings.cpp:497; IMAP/IMAPCommandCompress.cpp) -
IMAPSearchTimeout=60,IMAPSearchMaxMegabytes=2048 bound SEARCH/SORT/THREAD (source: IniFileSettings.cpp:347-349)
-
CAPA now includes
AUTH-RESP-CODE(pre2),IMPLEMENTATION hMailServer(pre2, no version),PIPELINING(pre2),EXPIRE NEVER(pre3),LOGIN-DELAY <n>(pre3, only whenPop3LoginDelaySeconds> 0) (source: hmailserver/source/Server/POP3/POP3Connection.cpp:493, 499, 508, 520, 532) - pre2: credential failures answer
-ERR [AUTH] …; inbox load failure-ERR [SYS/TEMP] …(RFC 3206) (source: POP3Connection.cpp:702-725, 795, 1027-1031) - pre3:
Pop3LoginDelaySeconds=0 (off) enforces a minimum login interval, early login refused-ERR [LOGIN-DELAY] …(source: IniFileSettings.cpp:619; POP3Connection.cpp:759). Not in README. - 6.2.14: refused-lock session no longer releases the owner's lock; RETR/TOP answer
-ERRon unreadable file;+ERRtypo fixed (source: v6.2.14 body) - 6.2.19 (Roadmap "13 August 2026"): seven POP3 conformance defects —
LIST n/UIDL nhide deleted messages,AUTH PLAIN =empty initial response, SCRAM failure firesOnClientLogon, CAPA no longer advertises USER/SASL on a connection that will refuse them (source: Roadmap.md:520 and the audit-defects table; commit ca788709b 2026-08-13) - APOP / CRAM-MD5 deliberately not implemented (need cleartext-equivalent stored secret) (source: pre2 body)
-
External POP3 fetcher: XOAUTH2 to hosts in
FetchOAuth2Hosts(defaultoutlook.office365.com), password is deliberately not a fallback (source: IniFileSettings.cpp:217; hmailserver/source/Server/ExternalFetcher/POP3ClientConnection.cpp:334; pre1 body). 6.2.15: rejected RETR no longer leaves an empty file; a downloaded message with no local recipients is removed and logged (source: v6.2.15 body) - 6.2.25:
SASL ... EXTERNALadvertised when the client certificate verified against the port's CA and names an address; the AUTH line and its SASL responses accept 12288 octets (was 500) (source: POP3Connection.cpp ProtocolCAPA_, :295-299) - 6.2.25: external accounts over IMAP —
FetchAccount.ServerType1 collects the remote INBOX once by UID (LOGIN or XOAUTH2, STARTTLS or TLS) and leaves it unlessDaysToKeepMessagessays otherwise; everything after the download is the POP3 fetcher's code (ExternalFetchClientBase) (source: hMailServer.idl:1854; Server/ExternalFetcher/IMAPClientConnection.h:32, ExternalFetchClientBase.h:27) - 6.2.27:
FetchAccount.MirrorFolders(schema 6031; CP Mirror every folder) — every remote folder collected into a local folder of the same name, byte for byte with\Seen \Flagged \Answered \Draft \Deleted, internal date and delimiter mapping, filed rather than delivered; per-folder collection record; days to keep messages 0 makes it a move (source: hMailServer.idl:1889; DBScripts/Upgrade6030to6031MSSQL.sql; v6.2.27 body)
-
Advertised
SIEVEcapability today:fileinto copy relational subaddress vacation vacation-seconds imap4flags body mailbox regex ihave environment date index spamtest spamtestplus duplicate editheader variables reject ereject include enotify envelope(source: hmailserver/source/Server/Common/Sieve/ManageSieveServer.cpp:332) -
envelopeis implemented and, since 6.2.25, advertised: theSIEVEline ends... include enotify envelope(source: ManageSieveServer.cpp:331;SieveEnvelopeDeliveryfixture, 5 tests) - 6.2.15: ManageSieve disconnects after three failed authentications and registers with auto-ban; 6.2.19-era audit fix: the ban is now enforced in the accept loop (source: v6.2.15 body; Roadmap audit table "ManageSieve brute force was unbounded")
- 6.2.19:
imap4flags(setflag/addflag/removeflag/:flags) actually applied at delivery (only the five system flags), advertised (source: ManageSieveServer.cpp:198; hmailserver/source/Server/SMTP/LocalDelivery.cpp:416; v6.2.19 body) - 6.2.21:
bodytest (RFC 5173) with:text/:content/:raw; Sieve trees move/delete with domain/account rename/delete; CP writes the script only after a successful save; unimplemented constructs are refused at upload naming the construct (source: v6.2.21 body) - pre1:
variables(5229),editheader(5293),duplicate(7352),reject/ereject(5429, real DSNs),include(6609),enotify(5435 mailto only),date/currentdatewith:index/:zone(5260),spamtest/spamtestplus(5235, from the server's own verdict),ihave/environment(5463/5183),mailboxexists+fileinto :create(5490),:regexwith the runaway-pattern circuit breaker; ManageSieve response codes;RENAMESCRIPTimplemented (source: ManageSieveServer.cpp:332, 940; pre1 body; Roadmap.md:598-618) - ManageSieve listener is inert by default:
ManageSieveServerPort=0, bind127.0.0.1(source: IniFileSettings.cpp:453-454) -
virustestdeliberately not implemented (source: Roadmap.md:613)
- 6.2.13: OpenSSL stays on the 4.0 line;
_WIN32_WINNT 0x0A00(source: v6.2.13 body). Today: OpenSSL 4.0.2, Boost 1.92.0, PostgreSQL 18.3 libpq, MariaDB Connector/C (source: hMailServer.vcxproj:76; build/Get-LibraryVersions.ps1:24-25); alpha2 named OpenSSL 4.0.1, Boost 1.91, PostgreSQL 18.3, MariaDB Connector/C 3.4.9 (source: alpha2 body) — both bumped in 6.2.25 (OpenSSL 4.0.2 is a security patch release) - 6.2.14: DKIM
t=yfailure → neutral (was pass); all key records at a selector examined; SNI failure reports the SNI error (source: v6.2.14 body) - 6.2.15: DKIM header-name case under
simplecanonicalisation fixed (upstream PR #530) (source: v6.2.15 body) - 6.2.19: custom DNS server regression (#25) fixed — 6.2.16–6.2.18 could not resolve anything through
DNSServer; aDNS_ADDRentry must carry port 0, not 53 (source: v6.2.19 body; IniFileSettings.cpp:401DNSServer="") - 6.2.19 (Roadmap 12–13 Aug): post-quantum key exchange —
TlsKeyExchangeGroupsdefaultX25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:secp256r1(source: IniFileSettings.cpp:572; Roadmap.md:424, 1398; commit 619ca858e); DANE validates the MX RRset as well as TLSA,DaneEnforcementEnabled=1 (source: IniFileSettings.cpp:403; Roadmap.md:319; commit ebc9305c9) - 6.2.21: DNS lookups whose answer Windows rejects as malformed retried once over TCP; CP MX tool uses the server resolver via
Utilities.ResolveMXRecords(IDL id 22) (source: IDL:1196; v6.2.21 body) - pre1: ACME http-01 challenge locator fixed (#34) — pretty-printed
"type": "http-01"never matched (source: pre1 body).AcmeEnabled=0,AcmeDirectoryUrlLet's Encrypt v2,AcmeHttpPort=80,AcmeReuseKey=1 (source: IniFileSettings.cpp:595-601) - pre2: TLS-RPT sending made verifiable —
Utilities.SendTlsRptReports(IncludeCurrentDay)(IDL id 23); refuses whenTlsRptFromAddress(default "") is unset (source: IDL:1205; IniFileSettings.cpp:483). Audit: reporter now logs at startup that statistics will never be sent while the address is empty (source: Roadmap audit table "TLS-RPT reporting is gated on an unset value") - pre2: DMARC aggregate (rua) reports — inert until
DmarcRptFromAddressset (default "");DmarcRptOrganizationName="hMailServer"; RFC 7489 §7.1 external-destination check; noruf;Utilities.SendDmarcReports(IDL id 24); data in memory (restart loses the day) (source: IniFileSettings.cpp:490-491; IDL:1209; pre2 body). CPViews/FeatureSettingsView.xaml.cs; not in README. - pre6: DMARCbis tree walk (RFC 9989 §4.10) replaces the PSL for organizational domain —
DmarcTreeWalkEnabled=1, PSL kept as fallback on transient failure; 8 queries/domain, 5-minute cache;np=tag honoured (NXDOMAIN vs NODATA); reports emit DKIM<selector>and SPF<scope>(source: IniFileSettings.cpp:639; pre6 body) - pre6: DKIM
rsa-sha1refused for signing and verifying (RFC 8301); signing domains re-signedrsa-sha256;DkimAcceptSha1=0 restores both (source: hmailserver/source/Server/Common/AntiSpam/DKIM/DKIM.cpp:974-976; IniFileSettings.cpp:642); RSA keys under 1024 bits treated as invalid (source: DKIM.cpp:268EVP_PKEY_bits(publicKey) < 1024) - pre6: SPF void-lookup limit
SpfVoidLookupLimit=2 (RFC 7208 §4.6.4) (source: IniFileSettings.cpp:640) - pre6: ACME renewal at two thirds of lifetime (one-day floor) and ARI (RFC 9773)
renewalInfoconsulted hourly (source: hmailserver/source/Server/Common/Util/AcmeClient.cpp:456-459, 934) - alpha1:
DmarcRptSchemaVersion=1 (RFC 7489);2emits RFC 9990 (dmarc-2.0namespace,versionelement stays1.0); invalid →HM6210and treated as 1 (source: IniFileSettings.cpp:506; README.md:440; alpha1 body) - ARC:
ArcSealingEnabled=0; 6.2.18 correction said relayed mail was never sealed; Roadmap says sealing is now reachable independently of author-domain signing, still gated on that setting (source: IniFileSettings.cpp:455; v6.2.18 body "Correction"; Roadmap.md "ARC sealing — corrected, then fixed"). DB settingASArcFilteringEnableddefault 0 (source: CreateTablesMSSQL.sql:995) - Inert-by-default pair stated at startup since the audit:
MtaStsHostingEnabled=1 butWebServicesHttpPort/HttpsPort=0, so MTA-STS/ACME hosting serve nothing until a port is set (source: IniFileSettings.cpp:602-607; Roadmap audit table "MTA-STS and ACME hosting were silently inert") -
SRSEnabled=0,BATVEnabled=0,AuthenticationResultsEnabled=0,ReceivedSpfHeaderEnabled=0 (all off) (source: IniFileSettings.cpp:480-481, 614, 616) - 6.2.15:
BOOST_USE_WINAPI_VERSIONraised to0x0A00(source: v6.2.15 body) - 6.2.25: ACME issuance and renewal ended the process (#93) — two calls handed the OpenSSL DLL a CRT
FILE*, which on Windows terminates the process for want ofOPENSSL_Applink; both now use OpenSSL BIOs, deployment runs before the TLSA line, and a certificate 6.2.24 issued but never deployed is deployed at the first check after start ("issued but never deployed" in the log) (source: AcmeClient.cpp:85,316,768BIO_new_file, :1581; v6.2.25 body) - 6.2.25:
/email.mobileconfigserved over HTTPS only — 301 to theWebServicesHttpsPortlistener, 403 when none, unlessX-Forwarded-Proto: https(source: WebServicesServer.cpp:653-659,758,807) - 6.2.27: a missing DS is proved missing (RFC 4035 §5.2, RFC 5155 §8) — the validating resolver requires the parent's NSEC/NSEC3/Opt-Out proof, signed by the parent's key, before calling a delegation unsigned; without one under a signed parent the chain is Bogus, not Insecure;
Diagnostics.DnssecChainStatusreports it (0 secure, 1 insecure, 2 bogus) (source: Server/Common/TCPIP/DnssecResolver.cpp; hMailServer.idl:3223)
- SMTP AUTH:
LOGINalways;PLAINonly if "allow plain text" is on;SCRAM-SHA-256always;-PLUSon TLS;XOAUTH2/OAUTHBEARERwhenOAuth2Enabled=1 (default 0,OAuth2RequireTLS=1) (source: SMTPConnection.cpp:2530-2552; IniFileSettings.cpp:208-209) - 6.2.14:
ES256OAuth2 tokens rejected with a clear message (source: v6.2.14 body). pre3: ES256 verifies (R||S → DER transcode), key type must match algorithm, and the algorithm must be inOAuth2AllowedAlgorithms(defaultRS256) (source: IniFileSettings.cpp:220; pre3 body) - 6.2.15 CP: Security ▸ Authentication page (OAuth2,
PreferredHashAlgorithm,MinimumAcceptedHashAlgorithm,PasswordPepper,DisableAUTHList= comma-separated local TCP ports) and Security ▸ Administrative access (admin password, 2FA setup) (source: v6.2.15 body; IniFileSettings.cpp:171, 198, 203, 674) - 6.2.19 (Roadmap 13 Aug): LDAP directory authentication incl. LDAPS with certificate validation, verified against a live Windows Server 2025 DC (source: Roadmap.md:627, 1117; commits ca788709b, d8976103b)
- 6.2.19: directory provisioning —
Settings.PreviewDirectorySync(DomainName, DisableMissing)/ApplyDirectorySync(source: IDL:785-787); domain opts in by having its AD domain name set; never deletes, at most clearsActivewhen asked; CP page + optional schedule (source: v6.2.19 body) - pre2: per-account lockout —
AccountLockoutThreshold=0 (off),AccountLockoutWindowMinutes=30,AccountLockoutMinutes=30; counts by the name guessed; locked-name refusals do not feed per-IP auto-ban (source: IniFileSettings.cpp:558-560; hmailserver/source/Server/Common/Util/AccountLockout.cpp; pre2 body). In CPViews/ServerSettingsView.xaml.cs; not in README. - pre3: app passwords (schema 6016) —
Account.AppPasswords(IDL id 42); 20 symbols from a 30-char alphabet (~98 bits); tried only after the account password fails (source: IDL:1012; DBScripts/Upgrade6015to6016MSSQL.sql; pre3 body) - pre3: per-account TOTP enforced by the server (schema 6017) — once
accounttotpsecretis set, the account password stops being a mailbox credential; app passwords remain (source: hmailserver/source/Server/Common/Util/PasswordValidator.cpp:174-189requiresSecondFactor = !pAccount->GetTotpSecret().IsEmpty(); pre3 body). Distinct from the administrator TOTP: until 6.2.24 the Control Panel checked that client-side; since 6.2.25 the server enforces it too — with[Security] AdministratorTotpSecretenrolled (Settings.EnrolAdministratorTOTP), COM needsCOMAuthentication.AuthenticateWithCodeand the REST API anX-hMailServer-OTPheader, so a stolen administrator password is no longer a complete credential (source: IniFileSettings.cpp:112; hMailServer.idl:810-811, 1720-1721; v6.2.25 body). - pre3: password policy —
PasswordPolicyMinimumLength,RequireMixedCase,RequireDigit,RequireNonAlphanumeric,RejectCommon(all 0 = off) plus the always-on "must not contain the account name"; enforced only where a password is chosen (source: IniFileSettings.cpp:666-670; hmailserver/source/Server/Common/Util/PasswordPolicy.cpp:170). Not in README. - pre3: password expiry and history (schema 6019) —
PasswordPolicyMaximumAgeDays=0,PasswordPolicyHistoryCount=0; until the self-service portal shipped in 6.2.28 (POST /api/v1/me/password) there was no self-service change, so expiry meant an admin reset; AD accounts exempt (source: IniFileSettings.cpp:671-672; pre3 body; RestApiServer.cpp:1720) - alpha1:
PreferredHashAlgorithmoutside 3, 4, 5 or 7 refused at read, PBKDF2 (4) substituted, reportedHM5528; default stays 4; 7 (scrypt, RFC 7914) added in 6.2.25 (source: IniFileSettings.cpp:190-209; alpha1 note 12) - Audit fixes (6.2.19 window): plaintext-stored passwords now upgrade on verify; POP3/IMAP/SMTP credential scrubbing in logs covers SASL initial responses and SCRAM continuations (source: Roadmap audit table)
- Not offered: SCRAM-SHA-1 (#49, closed not-planned — the stored PBKDF2-SHA256 output is the SCRAM-SHA-256 salted password) (source: alpha2 body)
- 6.2.25: SASL EXTERNAL on SMTP, IMAP and POP3 — a client certificate that verified against the port's CA logs on as the mailbox it names, through the addresses the certificate carries and nothing else (source: SMTPConnection.cpp:2665-2670 SendEHLOKeywords_; IMAPCommandCapability.cpp:122
AUTH=EXTERNAL; POP3Connection.cpp ProtocolCAPA_). The SMTP AUTH line limit rises from 510 to 12288 octets, RFC 5034's figure (SMTPConnection.cpp:476-483). - 6.2.25: scrypt as
PreferredHashAlgorithm=7, and schemes compared by strength rather than number (a preference for scrypt leaves Argon2id accounts alone; a minimum of either accepts both); work factorPasswordHashIterations,PasswordHashMemoryKB,PasswordHashTimeCost, a cheaper stored hash re-derived at the next logon, upward only (upstream #554) (source: Crypt.h:29ETScrypt = 7; IniFileSettings.cpp:206,233-235) - 6.2.25: OAuth2 JWKS and introspection —
OAuth2JwksUrl(cacheOAuth2JwksCacheSeconds=3600) as the signing-key source;OAuth2IntrospectionUrlwithOAuth2IntrospectionClientId/ClientSecret,OAuth2IntrospectionCacheSeconds=300,OAuth2IntrospectionFailOpen=0 for revocation; both off unless configured (source: IniFileSettings.cpp:265-275) - 6.2.25: tarpits —
LogonTarpitSeconds=0 on failed logons (SMTP/POP3/IMAP),SmtpTarpitCount=0/SmtpTarpitDelaySeconds=0 on stranger recipients (COMAntiSpam.TarpitCount/TarpitDelay, stubs for years); a pause on the connection's own timer, never a sleeping thread (source: IniFileSettings.cpp:675-679) - 6.2.25:
SmtpAuthenticatedSenderCheck=0 — when on, an authenticated session may only send as an address its account owns or has been granted; the post right on a mailbox's INBOX is the Send-As grant (source: IniFileSettings.cpp:734) - 6.2.25: event scripts can create only the COM classes
ScriptAllowedObjectsnames (default*); anything else fails inside the script with error 429 (source: IniFileSettings.cpp:389; Scripting/ScriptObjectPolicy.cpp)
- 6.2.11: EICAR test rewritten (ClamAV signatures match whole files) (source: v6.2.11 body)
- 6.2.13: SpamAssassin client parses responses defensively; original message preserved on any SA failure; scan ceiling clamped to the 80 MB MIME limit (source: v6.2.13 body)
- 6.2.19-era audit: spam pipeline no longer stops after one test when both thresholds are 0; a junk DKIM signature no longer erases a real failure;
b=located at a tag boundary (source: Roadmap audit table) - pre2:
AVFailAction—0(default) deliver with an error logged (historical behaviour),1hold, retry everyAVFailRetryMinutes=15 up toAVFailMaxHolds=16 then return to sender (source: IniFileSettings.cpp:537-551; pre2 body). CP yes; not in README. - pre3: quarantine (schema 6018) —
QuarantineEnabled=0,QuarantineRetentionDays=30; answers250and holds post-DATA verdicts; falls through to refusing if the store fails; release re-enters at the delivery queue; COMAntiSpam.Quarantine(IDL id 43); CP review page (source: IniFileSettings.cpp:624-625; IDL:2595; pre3 body). Not in README. - alpha1: external HTTP filter hook —
FilterHookUrl="" (empty = off),FilterHookTimeoutSeconds=10,FilterHookFailClosed=0,FilterHookRejectScore=100,FilterHookMaxMessageSizeKB=10240 (0= no ceiling); plain HTTP only; verdict is a score;HM5540non-http URL,HM5541engine down while fail-closed (source: IniFileSettings.cpp:658-662; alpha1 body). CPViews/ServerSettingsView.xaml.cs; not in README (see Contradictions). - alpha1: blocked senders (schema 6024) —
AntiSpam.BlockedSenders(IDL id 44);@= exact address, otherwise domain incl. subdomains at a label boundary; no wildcards; per-entry score default 100; null sender never blocked; matches the envelope sender; runs as spam check number one (log positions shift) (source: IDL:2596, 2815-2818; DBScripts/Upgrade6023to6024MSSQL.sql; alpha1 body) - alpha1: per-account spam settings (schema 6025) —
Account.AntiSpamEnabled(id 47),SpamMarkThreshold(id 48), delete threshold;-1= no override; act only on a score this server recorded (needs "add reason to header" on, which is the default); SMTP conversation unaffected (source: IDL:1018-1021; Upgrade6024to6025MSSQL.sql; alpha1 body) - alpha1: white-list cache actually caches, and deletes invalidate it (source: alpha1 body)
- alpha2: two bypasses closed — Received-header address selection (§4.1) and RSET/STARTTLS greeting state (source: alpha2 body)
- ClamAV timeouts:
ClamMinTimeout=15,ClamMaxTimeout=90 (source: IniFileSettings.cpp:282-283) - 6.2.25: spamd is told whose preferences to apply (
SpamAssassinUser="",SpamAssassinUserFromRecipient=0) and moving a message into the Junk folder teaches SpamAssassin spam, out of it ham (SpamAssassinLearnOnMove=0) through spamc's TELL into the local Bayes store, off the IMAP thread (source: IniFileSettings.cpp:341-354; SpamAssassinClient.cpp:69-73) - 6.2.25: the ClamAV connection test asks PING and VERSION first, names the daemon it reached, and streams its EICAR sample from memory — the button failed on every Windows with real-time protection because Defender removed the sample file first (source: ClamAVVirusScanner.cpp:136-157)
- 6.2.14: failed message copies no longer crash delivery; string SQL parameters on MSSQL/SQL CE bound from freed memory fixed; failed transaction start no longer leaks a pooled connection; message cache accounting fixed; account cache cleared on stop; domain size column fixed (source: v6.2.14 body)
- 6.2.15: restore confirms the replacement store exists before deleting; extracted copy kept on later failure;
.eml.tmporphans, bounce-file orphans, unbounded account cache fixed;BackupMessagesDBOnly(default 0) exposed on Backup & restore (source: v6.2.15 body; IniFileSettings.cpp:397) - 6.2.18: restore validates the archive before deleting the target (source: v6.2.18 body)
- 6.2.19: a colon in an account address is refused (local part becomes a directory name) (source: v6.2.19 body)
- 6.2.19:
[Settings]INI values mirrored into the database (hm_inisettings, schema 6011) so they can be administered remotely; the file still wins where both carry a value; falls back to the INI if the table cannot be read (source: hmailserver/source/Server/Common/Application/IniSettingStore.cpp:115-198; v6.2.19 body; Roadmap.md:925 "126 values, shipped 14 August 2026") - 6.2.19-era audit: deleting a group deletes its membership rows; IMAP folder-UID repair no longer aborts when the queue is non-empty; MIME parser nesting bounded (source: Roadmap.md:817, 827; audit table)
- pre1 (schema 6012): auto-reply begin date
Account.VacationMessageBeginDate(IDL id 41); rule criteria up to 2,000 chars (source: IDL:1032; Upgrade6011to6012MSSQL.sql) - pre6: quota warning
QuotaWarningPercent=90 (0disables), one notice per threshold crossing; texts inhm_servermessages(schema 6022) (source: IniFileSettings.cpp:643; Upgrade6021to6022MSSQL.sql) - pre6: archive retention
ArchiveRetentionDays=0 (never prunes; only.eml, no symlink following);ArchiveDir="" (source: IniFileSettings.cpp:262, 644) - alpha1: domain-wide out-of-office (schema 6024) —
Domain.VacationMessageOn/Subject/Message/InternalSubject/InternalMessage/VacationExternalOverride(IDL ids 51-55); "internal" = envelope sender resolves to a local account/alias (forgeable); RFC 3834 suppressions (Auto-Submitted, Precedence bulk/list/junk, List-*, X-Auto-Response-Suppress, mailer-daemon/postmaster) now apply to existing per-account replies too, with no switch (source: IDL:899-908; alpha1 note 10). Known: account vacation + Sievevacation= two replies (source: alpha1 limitation 5) - alpha1: distribution-list moderation —
DistributionList.ModeratorAddress(id 11): refused senders forwarded to the moderator withX-hMailServer-Moderation; approval = resend from an authenticated session;BounceAddress(id 12) = envelope sender for list copies (source: IDL:1365-1368; alpha1 body). Self-subscribe not included. Any credential holder can forge an approval (MAIL FROM is not bound to the account) (source: alpha1 limitation 1) - alpha1: privacy tooling, COM-only —
Account.ExportMessages(dir)(id 46, all-or-nothing) andUtilities.EraseAddressTraces(id 25;HM6250/HM6251) (source: IDL:1016, 1210; alpha1 body) - alpha1:
DatabaseStatementTimeout=30 s (0= none) — applied at connect on PostgreSQL/MySQL; reaches MSSQL/SQL CE only via maintenance-script connections; untested on MySQL/PostgreSQL (source: IniFileSettings.cpp:645; alpha1 note 7; v6.2.24 known limitation 1) - alpha1: delegated APPEND/COPY/MOVE filed under the wrong account (never shipped; fixed before alpha1) (source: alpha1 body)
- Scheduled backup knobs (pre-existing, findable):
ScheduledBackupTime,IntervalMinutes,KeepCount,MaxAgeDays(source: IniFileSettings.cpp:444-447) -
docs/WarmStandby.md(alpha1): exactly one service per database; DPAPI secrets do not travel; never rehearsed on two machines (source: alpha1 body; Roadmap.md:1437 still 🔄) - 6.2.25: message retention (schema 6027) —
Domain.MessageRetentionDaysandAccount.MessageRetentionDaysremove delivered mail older than the policy in every folder, by stored creation time, through the same deletion an EXPUNGE performs; off everywhere until a number is set;Utilities.RunMessageRetentionruns it now;MailboxRetentionTaskat start and every 6 h (source: DBScripts/Upgrade6026to6027MSSQL.sql; hMailServer.idl:854,988,1239; Application.cpp:673-680) - 6.2.25: archive index (schema 6029) — one
hm_archiveindexrow per copy, searched over COMUtilities.SearchArchiveandGET /api/v1/archive;SetArchiveHold//api/v1/archive/{id}/holdplaces a legal hold that survives the retention sweep andEraseAddressTraces;ArchiveDomains="" scopes the archive to named domains (source: Upgrade6028to6029MSSQL.sql; IniFileSettings.cpp:317; RestApiServer.cpp:2031; hMailServer.idl:1242-1243) - 6.2.25:
DeliveryHardLinks=0 — a message to several local recipients as one file with a name in each folder; every rewrite of a message file is a temporary file renamed into place (source: IniFileSettings.cpp:312) - 6.2.25: the schema enforces its parent-child relationships (schema 6030): seventeen
FOREIGN KEY ... ON DELETE CASCADE, orphans removed on the way in; columns where 0 is a value are deliberately without a key (source: Upgrade6029to6030MSSQL.sql) - 6.2.25:
BackupVerifyRestore=1 — every backup extracts its message store to a scratch directory through the restore code before it is called complete (source: IniFileSettings.cpp:480) - 6.2.25: TLS to the database is an INI line:
[Database] PostgreSQLSslMode,PostgreSQLSslRootCert,ConnectionStringOptions(MS SQL, appended verbatim),AllowUnencryptedConnection=0 (MySQL; upstream #559) — verified by reading, not on a live server (source: IniFileSettings.cpp:162-165; v6.2.25 known limitation 1) - 6.2.25: a Message-ID is added only for submissions (upstream #552); a stranded fetch account is unlocked by the task that owns it (upstream #603); the data-directory synchronizer walks the public folders (upstream #601) (source: SMTPMessageHeaderCreator.cpp:83-100; v6.2.25 body)
- 6.2.27: the Import Tool reads a Maildir (INBOX and every Maildir++ folder, flags from
:2,/;2,/!2,file names, line endings made CRLF); a message imported into an open folder appears at once;docs/Migration.mdcovers IMAP mirror, mbox, Maildir, Outlook via IMAP, upstream in place, bulk accounts (source: Tools/ImportTool/formChooser.Designer.cs:65; hmailserver/docs/Migration.md; v6.2.27 body)
- 6.2.11: alias list and live log announce content, not
+AliasRow/+LogLine, to screen readers (source: v6.2.11 body) - 6.2.12: DBSetup/DBSetupQuick/DBUpdater/DataDirectorySynchronizer/Shared → SDK-style .NET; COM consumed via checked-in tlbimp wrapper
source/Tools/Interop/; ImportTool (accounts from text file; mbox → IMAP folder viaUtilities.ImportMessageFromFileToIMAPFolder, IDL id 13) replaces the five VB6 wizards insource/Migration(folder no longer exists) (source:ls hmailserver/source/Tools; IDL:1181; v6.2.12 body) - 6.2.13: Diagnostics no longer reports every test FAILED; UI service restart is elevation-aware; DBUpdater labels 6002–6005 (source: v6.2.13 body)
- 6.2.14: Ctrl+K command palette searches settings by label and INI key (227 entries then; the generated index has 347 entries today); settings moved to Logging / scanner tabs / Performance ▸ Indexing / Delivery; "Advanced hardening" → "Advanced INI settings" under Maintenance (source: v6.2.14 body;
grep -con Services/SettingsSearchIndex.g.cs; MainWindow.xaml.cs, Services/NavigationMap.cs) - 6.2.15: new pages Security ▸ Authentication, Security ▸ Administrative access, Network ▸ DNS resolver, Network ▸ Web services & autoconfiguration, Advanced ▸ Copies of mail (archiving), Backup
BackupMessagesDBOnly, Diagnostics consistency-scan results; four labels corrected (UseDNSCache,DaemonAddressDomain,LogLevel,DisableAUTHList) (source: v6.2.15 body) - 6.2.16: Esc/selection in the palette no longer throws (#21) (source: v6.2.16 body)
- 6.2.19: directory-sync page; "pages show what is happening, including where a configuration looks enabled and is inert" (source: v6.2.19 body). Spam filtering overview page (Roadmap 13 Aug → 6.2.19) (source: Roadmap.md:1296; commit 8b5500c89)
- 6.2.21: restored from the retired Administrator: pause/resume engine, restore default ports, restore default IP ranges, delete domain, rename domain, rename account, inactive markers; MX tool via
Utilities.ResolveMXRecords; Welcome tiles centred (#30); DNS-cache and mailer-daemon descriptions corrected (UseDNSCachecontrols whether Windows' resolver cache may answer — there is no in-process cache) (source: v6.2.21 body; IniFileSettings.cpp:400) - pre1: Relayer page carries M365 OAuth settings; vacation begin date and criteria length editable (source: pre1 body)
- pre2: nine previously INI-only settings get fields (AV fail policy, lockout, DMARC report, POP3 OAuth host list); both OAuth host fields show the real default (a blank would have stopped M365 fetching) (source: pre2 body)
- alpha1: CP overhaul — Fluent type ramp, Segoe UI Variable, 168 MessageBox sites themed, 18 dialogs on a Fluent window, ~55 fields given accessible names; new Blocked senders page, Out of office domain tab, Windows Event Log card, PROXY protocol / XCLIENT card; sign-in no longer freezes on an unreachable host (narrowed, not closed); AccountDialog tabs reachable; duplicate tab names fixed; dark-theme chart cards; quarantine grid columns; three padded-text tables → DataGrids (source: alpha1 body)
- alpha1: COM vtable order repaired to match v6.2.21 exactly — five properties on
AntiSpam,Account,Application,GlobalObjectsmoved to the end of their interfaces (source: alpha1 note 4; Roadmap audit table) - New COM surface since 6.2.10 (all confirmed in IDL):
Settings.PreviewDirectorySync/ApplyDirectorySync(:785-787),Settings.MessageIndexing(:671),Domain.RelayHost…(:884),Domain.Vacation*(:899-908),Account.AppPasswords(:1012),Account.ExportMessages(:1016),Account.AntiSpamEnabled/SpamMarkThreshold/… (:1018-1021),Account.VacationMessageBeginDate(:1032),Utilities.ResolveMXRecords(:1196),SendTlsRptReports(:1205),SendDmarcReports(:1209),EraseAddressTraces(:1210),DistributionList.ModeratorAddress/BounceAddress(:1365-1368),Application.MessageTrace(:2362),AntiSpam.Quarantine(:2595),AntiSpam.BlockedSenders(:2596), coclassesAppPasswords,QuarantinedMessage,Quarantine,MessageTrace*,BlockedSender(s) - Control Panel + tools require the .NET 10 Desktop Runtime; the server is native (source: alpha1 note 15; csproj TFM)
- Service-account setting (
ServiceAccountName/Password) had never worked on any path; fixed 14 Aug 2026 (6.2.19) (source: Roadmap audit table; IniFileSettings.cpp:246-247) - 6.2.25: the Control Panel follows Windows High Contrast for every control (runtime, both ways); every static caption carries an Alt-key mnemonic, checked in CI; page titles are headings and status text a live region; the Welcome page starts from what you want to do; the stalled-mail diagnosis guide is a page; the status palette is held apart for colour-blind eyes by a test; "Advanced & scripting" is "Advanced" (source: v6.2.25 body; MainWindow.xaml.cs
ApplySavedTheme, :873; .github/workflows/style.yml:72check-mnemonics.py) - 6.2.28: the Control Panel is localised — resx catalogues with the English text as the key, complete translations in 17 languages (cs, da, de, es, fi, fr, it, ja, nb, nl, pl, pt-BR, ru, sv, tr, uk, zh-Hans), a language picker, and CI checkers that fail an unmarked or untranslated caption; an Updates card and three status-page buttons for the live update; every INI setting the server reads has an editor, checked by
build/check-ini-coverage.py; the password boxes' caret no longer jumps to the start (#156) (source: Tools/ControlPanel/Resources/Strings.*.resx; Services/LanguageChoice.cs; Views/StatusView.xaml.cs:42-44; Views/FeatureSettingsView.xaml.cs:1932; .github/workflows/style.yml:79-95)
- Listener inert by default:
RestApiPort=0, bind127.0.0.1; TLS mandatory unless loopback (source: IniFileSettings.cpp:591-594; alpha1 body) - 6.2.14: request-arrival bounded; rejected admin credential logged (source: v6.2.14 body)
- 6.2.19 (Roadmap 13 Aug): API keys —
hmapi_-prefixed bearer tokens stored as SHA-256 inhMailServerApiKeys.inibeside hMailServer.ini (source: RestApiServer.cpp:158ApiKeyTokenPrefix, :2416) - Audit fix: queue
retry/DELETEnow404for unknown ids; oldDELETEcould delete a mailbox message (source: Roadmap audit table; hmailserver/source/Server/Common/Util/RestApiServer.cpp:2944-2945) - alpha1 routes:
GET /api/v1/queue,POST /api/v1/queue/{id}/retry,DELETE /api/v1/queue/{id},GET /api/v1/srv(RFC 6186/8314 SRV records from the live port table — generates, publishes nothing),GET /api/v1/quarantine(newest 1000),POST /api/v1/quarantine/{id}/release,DELETE /api/v1/quarantine/{id},GET /api/v1/domains/{domain}/aliases,GET /api/v1/openapi.json(source: RestApiServer.cpp:1672-1774, 3116-3121) - alpha1: REST, metrics and web-services listeners accept IPv6;
::binds dual-stack;::1counts as loopback (source: alpha1 body; Roadmap.md:984) - Apple
.mobileconfigprofile served by the web-services listener (GET /email.mobileconfig, alias/mail/config.mobileconfig) — Roadmap "shipped 12 Aug 2026"; HTTPS-only since 6.2.25 (source: Roadmap.md:983; WebServicesServer.cpp:653-659) - 6.2.25 routes, read-only where COM is the writer:
GET/POST /api/v1/ipranges,DELETE /api/v1/ipranges/{id},GET/POST /api/v1/domains/{domain}/lists,DELETE /api/v1/lists/{id},GET /api/v1/domains/{domain}/dkim,GET /api/v1/certificates(never the private-key password),GET /api/v1/rules,GET /api/v1/logs,GET /api/v1/logs/{name},POST/GET /api/v1/backup,GET /api/v1/settings(snapshot),GET /api/v1/archive,POST/DELETE /api/v1/archive/{id}/hold,GET /api/v1/metrics/history— each decided at the same authorisation choke point (source: RestApiServer.cpp:1847,1930-2045; v6.2.25 body) - 6.2.28: the REST API and web services are re-hosted on
HttpServer(HTTP/1.1 on Boost.Asio with its own io_context, 4 workers, 64 connections, absolute deadlines, every API answerCache-Control: no-store);GET /api/v1/update,POST /api/v1/update/check|download|install; the self-service portal —/portal,POST/DELETE /api/v1/session,/api/v1/meand its password, vacation, quarantine, folders, messages, drafts, settings, filters and search routes, with a 16 MB / 300 s cap on the two routes that carry attachments (source: Common/Util/HttpServer.h:79-101; RestApiServer.cpp:128-136,1281-1284,1581-1738,1853-1871,2312)
- 6.2.14: logging settings consolidated on Logging (
LogLevel=9,MaxLogLineLen=500,SepSvcLogs=0,JsonLogging=0,LogDeleteDays=0) (source: IniFileSettings.cpp:251-253, 406-407; v6.2.14 body) - 6.2.17/6.2.18: per-stage acceptance timings; work-queue saturation reports name the task, session and peer IP;
AsyncQueueStallThreshold=120,AsyncQueueReservedThreads=2 (source: IniFileSettings.cpp:314-315; v6.2.18 body) - 6.2.18:
Logger::WriteLogFilerotation uninitialised-variable fix (source: v6.2.18 body) - pre1: with Keep log files open, every line is flushed to the OS as written (#33) (source: pre1 body)
- pre6: message trace (schema 6020) —
MessageTraceEnabled=0,MessageTraceRetentionDays=30;Application.MessageTraceCOM; CP page (source: IniFileSettings.cpp:626-627; IDL:1901-1909, 2362). Off "because it records who corresponds with whom" (source: pre6 body). Not in README. - pre6: per-domain metric labels —
MetricsPerDomainEnabled=0 addshmailserver_domain_messages_received_total{domain="…"}and_sent_total; only hosted domains are labelled (source: IniFileSettings.cpp:656; hmailserver/source/Server/Common/Util/MetricsServer.cpp:1605-1615). Not in README. - Prometheus listener inert by default:
MetricsServerPort=0, bind127.0.0.1, optional token/basic auth/TLS (source: IniFileSettings.cpp:426-437) - alpha1: Windows Event Log —
WindowsEventLogEnabled=1,WindowsEventLogLevel=2 (1 Critical, 2 +High, 3 +Medium, 4 +Low); source namehMailServer; throttle per event id; source registration is an HKLM write that succeeds under LocalSystem and fails silently under a lesser account; no message DLL (best-effortEventLogMessages.dll) (source: IniFileSettings.cpp:628-629; hmailserver/source/Server/Common/Application/WindowsEventLog.cpp:98, 255-263, 335; alpha1 note 6). Event ids 2010–2015 / 2000–2003: see Unconfirmed. - alpha1: OTLP metrics and logs —
OtelMetricsEndpoint="" andOtelLogsEndpoint="" (empty = off),OtelMetricsInterval=60 (clamped 5–3600); traces via pre-existingOtelEndpoint="",OtelServiceName="hmailserver"; metrics push carries event counters only — nine gauges only on/metrics(source: IniFileSettings.cpp:448-452; alpha1 limitation 13) - alpha1: inbound W3C
traceparentread from HTTP and SMTP message headers; never refuses mail (source: alpha1 body) - alpha1: 20 new
HMerror codes (5528, 5540, 5541, 6210, 6230, 6250-6252, 6260, 6280-6281, 6300, 6320, 6340, 6350-6351, 6360-6363) (source: alpha1 "New error codes" table) - Fault injection (INI-only, off):
SimulateSpoolWriteFailure=0,SimulateDatabaseFailureFor="" (source: IniFileSettings.cpp:422-423) -
MessageStoreConsistencyCheck=0 (source: IniFileSettings.cpp:425); Diagnostics page shows its results since 6.2.15 (source: v6.2.15 body) -
docs/DiagnosingStalledMail.md(6.2.18) — corrected in alpha1: it had told admins the custom-DNS setting was broken, which stopped being true in 6.2.19 (source: alpha1 body) - 6.2.25: the server remembers its metrics (schema 6028) — one row per metric per minute in
hm_metricsamples, keptMetricsHistoryDays=7, sampled byMetricsHistoryTaskat start and every minute, read back over COMUtilities.GetMetricHistory/SampleMetricsNowandGET /api/v1/metrics/history, shown on the dashboard for a day, a week or a month (source: IniFileSettings.cpp:735; Application.cpp:685-692; RestApiServer.cpp:1847; hMailServer.idl:1240-1241)
- 6.2.12: .NET Desktop Runtime is a server prerequisite (needed by the DB tools at post-install); .NET Framework 4.5 gate removed; tools ship as
dotnet publishfolders viabuild/build-tools.ps1; CI builds every C# project; NUnit 4 (source: v6.2.12 body) - 6.2.13: user-customised
EventHandlers.vbskept across uninstall/reinstall; failed DB create/upgrade propagates a real exit code; "Windows version too low" names Win10 1607 / Server 2016 (source: v6.2.13 body) - 6.2.17: installer smoke test on a clean throwaway machine (source: v6.2.17 body;
.github/workflows/installer-smoke.yml) - 6.2.19/6.2.20: release-signing workflow fixed; assets signed (source: v6.2.20 body;
sign-release.yml) - 6.2.20: all 28 installer dialogs take their default under
/SUPPRESSMSGBOXES; create scripts have no multi-statement commands (SQL CE) (source: v6.2.20 body) - 6.2.21: Postfix test rig
hmailserver/test/PostfixBenchin the repo (source:ls hmailserver/test) - pre6: immutable releases — assets (SPDX + CycloneDX SBOMs, cosign bundles) attached while draft; a spent tag cannot be reused;
check-db-scriptsstep builds a throwaway DB from the create script (source: pre6 body;sbom.yml) - alpha1:
prisma.yml(always-red sample workflow) removed; CodeQL extended to every shipped .NET tool; managed "Code quality" scan switched off; committed*.tlh/*.tliremoved and ignored (source: alpha1 body; workflow list has no prisma.yml) - alpha2: Control Flow Guard (
/guard:cf,<ControlFlowGuard>Guard</ControlFlowGuard>) in both configurations; Release also/Brepro(reproducible) (source: hmailserver/source/Server/hMailServer/hMailServer.vcxproj:87, 96, 164, 173) - alpha2: signed, annotated release tags (
git -c gpg.ssh.allowedSignersFile=.github/allowed_signers verify-tag v6.2.24); signing workflow refuses lightweight tags; DCO check on PRs (dco.yml);dotnet formatverify (style.yml);hmailserver/docs/RegressionEnvironment.md(#48) (source: alpha2 body; workflow list;ls hmailserver/docs) - alpha2: reproducible server binary — two clean builds byte-identical; v145 toolset (MSVC 14.51), SDK 10.0.26100 (source: alpha2 / v6.2.24 bodies)
- 6.2.24: NuGet lock files beside all nine .NET tool projects,
--locked-modein CI; eight required status checks on master (source: v6.2.24 body) - Installer is not Authenticode-signed (until 6.3.1, below); verification is by cosign bundle (source: v6.2.24 body)
- 6.3.1: the installer is Authenticode-signed with Azure Artifact Signing, countersigned with an RFC 3161 timestamp, publisher Progressive Robot Ltd; the release path gained a six-value readiness gate, verification of the release as attached, a required-asset assertion and per-tag serialisation (source: v6.3.1 body)
- Known then, fixed since: C++ CodeQL runs on the hosted
windows-2025-vs2026runner on every push to master, weekly and on demand (not on pull requests); C# runs per push and PR (source: .github/workflows/codeql.yml:5-30, 160-169) - 6.2.25: the C++ server is compiled on a GitHub-hosted runner on every push and pull request (
server-build.yml), with OpenSSL, Boost and libpq built from pinned, digest-verified sources (libraries/build-openssl.ps1,build-boost.ps1,build-pgsql.ps1) and cached; the weekly C++ CodeQL analysis runs — the second known limitation of 6.2.24 is closed; the fourteen open C++ findings dealt with; Scorecard published to the OpenSSF API;build/analyze.ps1/analyzebuild with its 632 findings triaged (source: .github/workflows/server-build.yml;ls libraries/; v6.2.25 body) - 6.2.25: a Release build that keeps its assertions (
build.ps1 -Asserts, HM6364) is the dynamic-analysis build the suite runs against before a release (RELEASE.md step 8b), and a timed fuzz run is a release step (8c) — the harness had not built since 13 August (source: RELEASE.md:84-105; hmailserver/docs/Fuzzing.md:358-361) - 6.2.26:
build/check-db-scripts.ps1executes every DBUpdater probe against the database it builds, through the provider the server uses, with a negative control; a regression fixture runs them through the COM path against the SQL CE bench (source: v6.2.26 body; build/check-db-scripts.ps1:126-156)
None of the following was in a published release before v6.2.28; all of it ships there, in the 47 commits after v6.2.27 (git log v6.2.27..v6.2.28). The schema does not move: 6.2.28 is still 6031.
-
Live update:
UpdateCheckTaskreads the release feed once a day whenUpdateCheckEnabled=1 (on demand always:Status.CheckForUpdate,POST /api/v1/update/check);DownloadUpdatefetches the installer and its Sigstore bundle and keeps the installer only when the bundle verifies (Common/Util/SigstoreVerifier.cpp — no cosign, no network beyond the download);InstallUpdatehands it tohMailServer.Updater.exe, which runs it, waits for the service, rolls back if it does not return, and reports the outcome at the next start; DBUpdater authenticates with a single-use token; fourteenUpdate*INI keys (IniFileSettings.cpp:489-509), COMStatus.UpdateState...UpdateApplyOutcome(hMailServer.idl:540-551),GET /api/v1/update, an Updates card and three status-page buttons in the Control Panel (source: Application.cpp:709-715; Server/hMailServer.Updater/; installation/section_files_64.iss:6). -
HttpProxy([Settings], default "" = a direct connection): a forward proxy for every web request the server makes as a client — the update feed, the installer and its bundle, and, through the sameHttpsClient, the OAuth2 JWKS fetch and token introspection.host:port, or[ipv6]:port; a value without a port is an error rather than a silent direct connection, and there are no proxy credentials. An https target is reached with CONNECT and the TLS handshake runs inside the tunnel under the same certificate verification as a direct connection; plain http gets the absolute URL in the request line; a proxy that says no is reported as "The proxy refused CONNECT to host:port: ". The editor is on the Control Panel's Updates card beside the feed URL (source: Common/Util/HttpsClient.cpp:86-198; IniFileSettings.cpp:492; FeatureSettingsView.xaml.cs:1936). -
IMAP COMPRESS=DEFLATE (RFC 4978):
IMAPCompressionEnabled=1 advertises it; zlib 1.3.1 vendored underServer/zlib(source: IMAPCommandCapability.cpp:61-62; IniFileSettings.cpp:497; IMAP/IMAPCommandCompress.cpp). -
HTTP foundation:
Common/Util/HttpServer.cpp— HTTP/1.1 on Boost.Asio with its own io_context, 4 worker threads, 64-connection cap, absolute per-request (30 s) and per-connection (300 s) deadlines, a 64 KB request cap with a 16 MB / 300 s cap a filter grants per request; REST API and web services re-hosted on it; every API answerCache-Control: no-store; every listener still builds its TLS context throughSslContextInitializer::InitServer(source: HttpServer.h:79-101; RestApiServer.cpp:128-136,618). -
Self-service portal:
/portaland/api/v1/me*on the REST listener — credentials, browser sessions (/api/v1/session, cookie known by its hash, bounded, CSRF header), own quarantine, folders and messages (read, flags, move, delete, attachments as downloads, search, drafts, send with attachments through the SMTP-submission pipeline, Sent copy), settings and the active Sieve script; an account with a TOTP secret sendsX-hMailServer-OTP; inert whileRestApiPort=0 (source: RestApiServer.cpp:1281-1738, 7660-7677). -
Control Panel localisation: resx catalogues, 17 languages complete and held complete by CI, a language picker (
Services/LanguageChoice.cs);build/check-ini-coverage.pyproves every INI setting has an editor (style.yml:95); password-box caret fix (#156); and the two text editors that shipped without a default no longer drop what they could not show — saving the SSL/TLS page kept the post-quantum key exchange groups, saving the Scripting page kept every script's COM objects. -
The Control Deck reads four things: the browser page at
/gained a log list with any file's last 200 or 2,000 lines, the server-wide settings as a table, every certificate by name and file (never a key password), and the global rules in the order the server evaluates them — all read-only, all reachable with a read-only key. It is served as the bytes on disk, so its own glyphs survive, with a Content-Security-Policy that allows only its own inline script and style,X-Content-Type-Options: nosniffandReferrer-Policy: no-referrer(source: installation/WebAdmin/index.html; RestApiServer.cpp:2977-3001).
-
6.3.0 - Linux. The server builds with CMake on Ubuntu with clang or GCC, for x86-64 and AArch64; the Linux build workflow makes
.deb,.rpmand AppImage packages and installs the.debon every run; the regression suite runs against a Linux server (net10.0, the COM object model shimmed onto/api/v1/…; 756 passed, 649 skipped of 1,405 on the 6.3.0 tag).[Directories]may be relative to the program folder. The schema stays 6031. -
6.3.1 - the signed installer.
hMailServer-6.3.1-x64.execarries an Authenticode signature (Azure Artifact Signing, publisher Progressive Robot Ltd, RFC 3161 countersigned);UpdateRequireAuthenticode=1 becomes usable. Nine release-path defects fixed after executing the real workflow bodies against the live asset list - the Authenticode gate checked the wrong settings, verification could pass vacuously, runs were not serialised per tag, the SBOM could be overwritten after signing, the smoke test could not read a draft. Known and named unfixed there, fixed in 6.3.2: the 6029→6030 upgrade step's orphan sweep ran children before parents.
Everything below ships in 6.3.2 and was in no published release before it. The schema moves from 6031 to 6038 in seven steps (section 2); DBUpdater runs them.
-
The webmail became a mail client (
/portal, everything over/api/v1/me; the OpenAPI document at/api/v1/openapi.json). Reading: conversations by thread; Archive, Junk and Trash as one-key actions (a move into or out of Junk teaches the spam filter); a search written as a reader writes it (from:,to:,subject:,has:attachment,before:,after:,in:,is:unreadand the rest,label:); labels as IMAP keywords (schema 6036) that every client sees; image and PDF previews; link and look-alike-sender warnings; shared and public folders. Writing: a formatting bar and an HTML editor; a From picker over the account's identities; signature; templates with{first_name},{subject},{date}; undo send; read receipts (RFC 8098); one-click unsubscribe (RFC 8058) to public addresses only; send later and snooze (schema 6034); an address book with To-field completion (schema 6032); files above a size as links that expire and can take a password (schema 6035). S/MIME in the browser with the Web Crypto API only: PEM, DER or PKCS#12 in, the private key wrapped under the account password before the server stores it (schema 6037), signatures verified against the system's roots, encryption and decryption in the page. Pages: Settings (name, signature, theme, density, notifications, language - the Control Panel's seventeen plus Greek, Korean and European Portuguese), Away, Filters (a rules editor that writes Sieve), Contacts, Storage, Files, Security (app passwords made only with the account's own password proven again, browser sessions, support access, the S/MIME key store), held mail. The page installs as an app, keeps the inbox listing and thirty opened messages offline, queues a send without a connection, carries the server's branding and announcement (PUT /api/v1/portal/branding), imports.emland exports mbox, works with a screen reader.hmailserver/docs/WebmailShortcuts.mdlists the keys and the search operators. -
SASL GSSAPI (RFC 4752) on SMTP, IMAP and POP3, on Windows:
GssapiEnabled=1 in[Settings]; the process's own credentials on a domain-joined host, orGssapiServiceAccountandGssapiServicePassword; the client's principal names the account it is linked to, elseuser@realm. Proven end to end against a Windows Server domain controller. -
Auto-ban reaches the operating system's firewall:
AutoBanFirewall=1 keeps a Windows Defender Firewall block rule per banned address (a packaged nftables hook and a fail2ban filter and jail on Linux),AutoBanCommandruns anything else,AutoBanNeverBanlists addresses never banned. All off as shipped. -
A SURBL server's answers are judged like a DNSBL's:
SURBLServer.ExpectedResult(schema 6038; the Control Panel's SURBL editor) in the DNSBL syntax; with none set any answer counts except the 127.255.255.0/24 codes with which the Spamhaus zones refuse a query, which were taken as listings until now (discussion #167). -
Linux: Boost linked statically, so the
.deband.rpminstall on the next distribution release (Ubuntu 26.04 proven in CI); a container image on GHCR from every tag, run as its own user, smoke-tested beside PostgreSQL over TLS; the three Windows-only calls the webmail waves had used gained POSIX counterparts. -
Database: the 6029→6030 step sweeps parents before children in all four dialects, and a gate now winds a created database back to 6029, seeds orphans and upgrades it on PostgreSQL and MySQL on every push; the create scripts drop dependent tables before
hm_accounts; every statement in a MySQL script is followed by a blank line, whichbuild/check-sql-separators.pyenforces after two schema steps had broken the MySQL create script between waves 10 and 15 (caught by the gate before any release carried them); the contacts index is unique. -
A server holding a database it refused answers COM with the refusal (HM5011 text, which names DBUpdater) rather than an access violation;
hMailServer.Databasestill works, which is DBUpdater's path. -
Build and supply chain: thirty-seven of forty committed binaries left git (fetched or gathered against the manifest at build time; the COM wrapper generated from the type library);
.sigstore.jsontwins and SLSA provenance beside every cosign bundle; a Windows job re-verifies the Authenticode signatures the manifest records; Harden-Runner on every Linux job; Dependabot for Actions and .NET. -
Quality: Code Quality at zero findings; the
/Debugconsole server reachable over COM and a suite mode for it (HM_CONSOLE_SERVER=1), so native coverage can be measured; the pre-flight refuses to pass while WSL runs; the test SMTP simulator reads a large message in linear time. -
Documentation and governance:
SECURITY.mdstates the vulnerability-management thresholds;GOVERNANCE.mdnames two maintainers; the release checklist says releases are immutable once published; README describes the webmail as it is and shows the Control Panel and the webmail; the ADO type-library licence question is closed (nothing of Microsoft's is redistributed).
Everything below ships in 6.3.3 and was in no published release before it; the full suite on the stamped binary: 2,302 tests, 2,294 passed, 0 failed, 8 skipped. Each of the three batches that carried it went through the full Windows suite on its exact tree before it landed: 2,267 tests, 2,259 passed, 0 failed, 8 skipped on the first, 2,279 tests, 2,268 passed, 3 failed, 8 skipped - the three put right in the commits above it on the second, 2,301 tests, 2,293 passed, 0 failed, 8 skipped on the third. The schema moves from 6038 to 6040 in two steps (section 2): 6039 widens a domain's relay-password column, 6040 adds the CardDAV columns; DBUpdater runs them.
-
The Linux regression suite's route backlog (waves A to H, 13 and 14 September): a REST route for every family the Linux fixtures had skipped - the directories and the ini section of the settings, the logon-failure list, domain aliases, a domain read whole and renamed, an IP range updated in place, the server messages, Sieve evaluation, fetch accounts, scripting, the backup settings, an account read whole, the caches, the message index and the relayer's password - and a build object on
/api/v1/status(script_engine,argon2id,post_quantum_key_exchange). The hosted Linux run after waves A to F: 1,457 tests, 1,185 passed, 0 failed, 272 skipped; on the tree with everything on this list: 1,493 tests, 1,246 passed, 2 failed, 245 skipped - the two put right above it. Three server defects on the way: a vacation save PostgreSQL refused,ncsarefused as a log format, a negative thread id on Linux; and one on every Windows installation, found by the first honest Windows gate of the branch: a domain's relay password could not be saved (hm_domains.domainrelaypasswordwas 255 characters, a DPAPI envelope is 314; schema 6039 widens it). -
One header on every source file, the project's, in one form, put there and checked by
build/add-license-headers.py. -
Two defects the tidy branch's own gates found:
generate-com-wrapper.ps1died under Windows PowerShell the first time its MIDL path ran (MIDL's stderr became a terminating error); a COM error out of a recordset walk ended a scheduled task with a minidump and no description -ADORecordset::MoveNextcatches it now (HM5037) and the exception handler names COM errors. -
An outbound BDAT chunk larger than one send buffer no longer stalls (issue #261, reported and fixed 14 September). The connection sends 60,000 bytes at a time; the SMTP client armed the read for the reply as soon as the BDAT command - or, with PIPELINING, the envelope - had been queued, and the second buffer of the chunk was queued behind that read in a queue that starts only its head, so nothing sent it and the remote timed out. Every message over 60,000 bytes to a remote advertising CHUNKING (Gmail, iCloud, Outlook.com, Postfix) had stalled this way since outbound BDAT arrived in 6.2.28; short ones went.
TCPConnection::ProcessOperationQueue_runs once more after a read starts; two tests relay a 222 KB message over BDAT with and without PIPELINING. Before 6.3.3,OutboundChunking=0was the workaround. -
On PostgreSQL a backslash is stored as one backslash (found by the hosted Linux run of the third batch). Every value with a backslash - a Windows path in a DKIM key-file setting, a backslash in a signature, a vacation message or a rule - was stored doubled, because the escaper doubled it for PostgreSQL unconditionally, which is right only while
standard_conforming_stringsis off, and it has been on by default since 9.1. The connection reads the server's answer when it comes up and doubles only where the server escapes. Values already stored doubled stay so until saved again. - An upgrade from a schema older than 6038 works again (issue #263, found and fixed 14 September). 6.3.2's guard, which makes a database the server has refused (HM5011) answer COM with the refusal instead of crashing, covered the whole Settings object, and DBUpdater reads the script directory from it before its first script - so every upgrade from 5.x or 6.2.x stopped with "The server has not loaded its configuration" and left the database untouched. The settings object answers its directories on a refused database again, everything in the configuration is refused with the reason, DBUpdater derives the script directory from its own location if the server cannot answer, and a regression test walks its path over COM against a refused database.
- The live update from an open Control Panel: the first real update, 6.3.1 to 6.3.2, failed twice with Inno's exit code 5 because the Control Panel that started it kept its own files open. The helper now ends whatever runs from under the installation before each installer run, the Control Panel closes itself once the helper has started, and a failed run's outcome quotes what the installer's own log says went wrong. An installation on 6.3.1 or 6.3.2 must be updated by hand once, since the helper that runs a live update is the one already installed.
-
CardDAV (RFC 6352) on the web-services listener: the account's address book to phones and desktop clients, found by
/.well-known/carddavdiscovery, over HTTPS with the account's password or an application password; the card a client sends is the card it reads back. -
The webmail rebuilt to the shape of Gmail and Outlook.com: a reading pane (right, below, off), a list with sender, subject, snippet, time and hover actions, a docked compose window with the reply written under the message, conversation cards, tabs (Primary, Social, Promotions, Updates, Forums - the server says which from the header) or Focused and Other, mute, pin, block, sweep, drag-and-drop to folders, a right-click menu, select-all across a whole folder, an attachment reminder, shift-click and ctrl-click selection, search history, pop-out windows, and twelve more search operators (
cc:,bcc:,filename:,larger:,smaller:,older_than:,newer_than:,is:muted,is:pinned,category:,-word,OR). Twenty languages throughout. -
The Linux control panel, measured and moved:
build/check-deck-parity.pycounts every field the desktop Control Panel writes against the REST API and the browser Control Deck (335 properties; 240 writable over REST and 153 reachable in the Deck at the start of the day, 306 and 248 by its end). New write routes: the anti-virus settings group, sixteen more account fields, nine more domain fields, the distribution list's settings and aPUTfor it, and DNS blacklists, SURBL servers, white-list addresses, blocked senders and incoming relays as resources. The Deck gained a harness of its own (build/check-deck-script.py, in CI), full domain editing, an IP-ranges view, and fetch-account and backup views. -
The webmail's next six (the fourth batch):
has:link(an address in the text or anhrefin the HTML) andin_reply_to:as search operators; a contact's name completing tofrom:<address>under the search box; follow-up flags with a date ($FollowUpand a$Due-YYYY-MM-DDkeyword on the message, so other clients still see a flag), a Starred view sorted soonest first and a reminder once a day; nudges - "Received N days ago. Reply?", "Sent N days ago. Follow up?"; quick steps (move, mark read, label, forward in one press, on the digit keys); and Clean up conversation, deleting what a later message quotes whole. The page's harness runs 224 checks. -
The Control Deck to parity (the fourth batch): the cache ceilings and lives, an IP range's expiry, blocked attachments and the greylisting white list as resources,
abort_spam_flagged,user_interface_language, and views for the account in full, distribution lists, aliases, the seven small collections, server messages and the scripting, cache and indexing groups - every property writable over REST that a Deck view covers, it reaches (322 of 328); the census itself corrected for what it had misread. The Deck's harness runs 292 checks. -
The Linux suite's last route families (the fourth batch): application passwords administered under
/api/v1/accounts/{address}/app-passwords, folder permissions under.../folders/{id}/permissions, and the message object - a folder's live collection, a message as a row with every header, a raw.emladded on APPEND's path, flags on STORE's, a delete on EXPUNGE's - with the shims rewritten. The hosted Linux run on that tree: 1,520 tests, 1,289 passed, 0 failed, 231 skipped. - Roadmap2.md: the programme after 6.3.2, linked from Roadmap.md.
The 6.2.24 seven, verbatim in substance (source: v6.2.24 body "Behaviour that changes on upgrade, without a switch"):
-
MAIL FROM/AUTHafter STARTTLS without a freshEHLO→503 Bad sequence of commands(source: SMTPConnection.cpp:527, 554). - Relayed/fetched mail: anti-spam tests run against the address the relay observed, not the HELO literal (source: alpha2 body).
- Mail refused with a temporary error below 100 MB free —
MinimumFreeDiskSpaceMB=100; set 0 to restore (source: IniFileSettings.cpp:417). - Critical/High errors also go to the Windows Application event log —
WindowsEventLogEnabled=1 (source: IniFileSettings.cpp:628). - Existing per-account out-of-office replies honour RFC 3834 suppressions (source: alpha1 note 10).
- One bulk prune of
hm_imapexpungedat first start —IMAPExpungeRetentionRecords=5000; set 0 first to defer (source: IniFileSettings.cpp:646). - Early-bound COM clients built against a 6.2.22 pre-release interop must be recompiled (source: alpha1 note 4).
The 6.2.25 eight (source: v6.2.25 body "Behaviour that changes on upgrade, without a switch"):
8. /email.mobileconfig is served over HTTPS only — 301 to the HTTPS listener when configured, 403 when not, unless a proxy sends X-Forwarded-Proto: https (source: WebServicesServer.cpp:653-659,758,807).
9. A Message-ID is no longer added to relayed mail, only to submissions (source: SMTPMessageHeaderCreator.cpp:83-100).
10. With IMAP ACL enforcement off, a rule or a delivery into a public folder is allowed, as every IMAP command already allowed it (source: ACLManager::CheckPermission, Common/Application/ACLManager.h:49).
11. The four HTTPS clients (ACME, OAuth2 token, MTA-STS fetch, introspection/JWKS) require TLS 1.2 or later whatever the mail-protocol toggles allow (source: v6.2.25 body; not located by grep in HttpsClient.cpp).
12. The AUTH command and its SASL responses accept lines of up to 12288 octets on SMTP and POP3 (were 510 and 500) (source: SMTPConnection.cpp:483; POP3Connection.cpp:299).
13. IMAP sequence numbers are stable within a session (upstream #602).
14. Account.DeleteMessages empties a designated special-use folder and keeps it, as it always kept the inbox.
15. A DANE TLSA line that cannot be computed after an ACME issuance is logged as such; the certificate is deployed first either way.
6.2.26 and 6.2.27 change no behaviour without a switch (6.2.27's schema step adds one defaulted column).
The 6.2.28 four:
16. IMAP advertises COMPRESS=DEFLATE and compresses both directions of a session that asks for it — IMAPCompressionEnabled=1; set 0 to withdraw the capability (source: IniFileSettings.cpp:497; IMAPCommandCapability.cpp:61-62).
17. The REST API and the web services are served by HttpServer: 64 KB per request, 64 concurrent connections and absolute 30 s per-request / 300 s per-connection deadlines on REST, 128 connections and 15 s / 120 s on web services, and Cache-Control: no-store on API answers. A request that outlives its deadline is closed without a response, where before nothing bounded it (source: HttpServer.h:79-101; RestApiServer.cpp:128-136; WebServicesServer.cpp:45-49).
18. Wherever the REST listener is enabled, GET /portal answers with a sign-in page and an account's own address and password reach the /api/v1/me routes. There is no separate switch: the portal is inert only while RestApiPort=0 (source: RestApiServer.cpp:1281-1284, 1581-1738).
19. The Control Panel opens in the Windows display language when one of the seventeen translations matches it; the language picker chooses another, English included (source: Services/LanguageChoice.cs).
Earlier ones an upgrader from 6.2.10 also gets:
- 6.2.14: DKIM
t=yfailures are neutral; IMAP SASL credentials masked; settings relocated in the CP (source: v6.2.14 body) - 6.2.15: IMAP
*andn:msemantics per RFC 3501 (UID STORE *:*addresses one message) (source: v6.2.15 body) - 6.2.17: acceptance bounded at 240 s →
451 4.3.1(source: IniFileSettings.cpp:281) - 6.2.18: DB-unavailable recipient lookup →
451;DBConnectionAcquireTimeout=60; outbound sessions capped at 30 min; DNS/script/external-process timeouts (source: IniFileSettings.cpp:293-308) - 6.2.19:
[Settings]mirrored tohm_inisettings(file wins); TLS key-exchange groups default to hybrid ML-KEM first; Sieveimap4flagsnow takes effect; account addresses with:refused (source: IniFileSettings.cpp:572; IniSettingStore.cpp; v6.2.19 body) - 6.2.20:
THREAD=…advertised unconditionally (source: IMAPCommandCapability.cpp:44-49) - 6.2.21: Sieve trees move/delete with renames/deletes; unimplemented Sieve constructs refused at upload; SORT with empty criteria rejected (source: v6.2.21 body)
- pre1: EHLO on every outbound delivery;
SIZE=declared; oversize refused locally; every literal{n+}accepted without continuation; kept-open logs flushed per line (source: SMTPClientConnection.cpp:588; pre1 body) - pre2: POP3
-ERR [AUTH]/[SYS/TEMP]prefixes;PIPELINING/IMPLEMENTATIONin CAPA; faster idle shutdown (source: POP3Connection.cpp:493-508; pre2 body) - pre3:
EXPIRE NEVERin CAPA; ES256 tokens verify once allow-listed; passwords chosen from now on must not contain the account name (source: POP3Connection.cpp:520; PasswordPolicy.cpp:170) - pre6: DMARC organizational domain by tree walk (
DmarcTreeWalkEnabled=1);np=honoured;rsa-sha1DKIM refused and re-signed asrsa-sha256; SPF void lookups capped at 2; ACME renewal at 2/3 lifetime + ARI; full mailbox refused at RCPT with452 4.2.2; quota warning at 90 %; DMARC report ids and<selector>(source: IniFileSettings.cpp:639-643; DKIM.cpp:974; AcmeClient.cpp:456-459) - alpha1: DSNs are
multipart/report; blocked-sender check is spam test number one (log positions shift);PreferredHashAlgorithm< 3 refused;DatabaseStatementTimeout=30 on PostgreSQL/MySQL;#Usersnamespace reachable whereverenableimapacl=1; every IMAPLISTruns one more query (source: alpha1 notes 5-12)
-
source/Migration(five VB6 wizards, three for dead products) — replaced bysource/Tools/ImportTool(6.2.12) (source:ls hmailserver/source; v6.2.12 body) - .NET Framework 4.5 installer gate (6.2.12); .NET Framework 4.8.1 targets for the tools (6.2.12); .NET 8 targets (6.2.19, → .NET 10) (source: v6.2.12 body; Roadmap.md:184)
- Windows 7/8 support: floor is Windows 10 1607 / Server 2016 (6.2.13) (source: section_setup.iss:28)
-
prisma.ymlworkflow (alpha1) (source: alpha1 body;.github/workflowslisting) - Committed
msado28-x64.tlh/.tli(alpha1) (source: Roadmap audit table) - GitHub-managed "Code quality" buildless scan (alpha1) (source: alpha1 body)
- DKIM
rsa-sha1signing/verification by default (pre6) (source: DKIM.cpp:974) - Fixed 30-day ACME renewal window (pre6) (source: AcmeClient.cpp:456-459)
- Pretty-printed-JSON-incompatible ACME challenge locator (pre1) (source: pre1 body)
- Per-version change history in README (6.2.17: "now lives on the Releases page") (source: v6.2.17 body)
- Releases
v6.2.22-pre4andpre5(withdrawn/superseded; tags remain) (source: pre6 body;git tag -l) - (Before scope, for context) WebAdmin and the Administrator were removed in 6.2.10 (source: v6.2.10 release title)
All read in IniFileSettings.cpp with the default shown; "CP" = a view file other than the generated index references the key; "README" = key present in README.md's annotated [Settings] block (checked 2026-09-04). Every key below is in the Ctrl+K index.
| Key | Default | First release | CP view | README | Source line |
|---|---|---|---|---|---|
FinalizationTimeout |
240 | 6.2.17 | FeatureSettingsView | no | :281 |
DBConnectionAcquireTimeout |
60 (0 in 6.2.17) | 6.2.17/18 | FeatureSettingsView | no | :306 |
ClientSessionCeiling |
1800 | 6.2.18 | FeatureSettingsView | no | :299 |
DNSQueryTimeout |
10 | 6.2.18 | FeatureSettingsView | no | :293 |
AutoBanFirewall |
0 | 6.3.2 | settings index | no | AutoBanFirewall.cpp |
AutoBanCommand |
(empty) | 6.3.2 | settings index | no | AutoBanFirewall.cpp |
AutoBanNeverBan |
(empty) | 6.3.2 | settings index | no | AutoBanFirewall.cpp |
GssapiEnabled |
0 | 6.3.2 | — | no | GssapiAcceptor.cpp |
GssapiServiceAccount |
(empty) | 6.3.2 | — | no | GssapiAcceptor.cpp |
GssapiServicePassword |
(empty) | 6.3.2 | — | no | GssapiAcceptor.cpp |
ScriptTimeout |
60 | 6.2.18 | FeatureSettingsView | no | :307 |
ExternalProcessTimeout |
300 | 6.2.18 | FeatureSettingsView | no | :308 |
TlsKeyExchangeGroups |
X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:secp256r1 |
6.2.19 | (not checked) | (not checked) | :572 |
OutboundOAuth2TokenUrl / ClientId / ClientSecret / FixedToken
|
"" | pre1 | ServerSettingsView | no | :210-216 |
OutboundOAuth2Scope |
https://outlook.office365.com/.default |
pre1 | ServerSettingsView | no | :214 |
OutboundOAuth2Hosts |
smtp.office365.com |
pre1 | ServerSettingsView | no | :215 |
FetchOAuth2Hosts |
outlook.office365.com |
pre1 | ServerSettingsView | no | :217 |
DmarcRptFromAddress |
"" (inert) | pre2 | FeatureSettingsView | no | :490 |
DmarcRptOrganizationName |
hMailServer |
pre2 | — | no | :491 |
AccountLockoutThreshold |
0 (off) | pre2 | ServerSettingsView | no | :558 |
AccountLockoutWindowMinutes / AccountLockoutMinutes
|
30 / 30 | pre2 | ServerSettingsView | no | :559-560 |
AVFailAction |
0 (deliver) | pre2 | ServerSettingsView | no | :537 |
AVFailRetryMinutes / AVFailMaxHolds
|
15 / 16 | pre2 | — | no | :544, :551 |
QuarantineEnabled |
0 | pre3 | ServerSettingsView, QuarantineView | no | :624 |
QuarantineRetentionDays |
30 | pre3 | — | no | :625 |
Pop3LoginDelaySeconds |
0 (off) | pre3 | ServerSettingsView | no | :619 |
PasswordPolicyMinimumLength / RequireMixedCase / RequireDigit / RequireNonAlphanumeric / RejectCommon
|
0 | pre3 | ServerSettingsView | no | :666-670 |
PasswordPolicyHistoryCount / PasswordPolicyMaximumAgeDays
|
0 / 0 | pre3 | ServerSettingsView | no | :671-672 |
MessageTraceEnabled / MessageTraceRetentionDays
|
0 / 30 | pre4→pre6 | ServerSettingsView, MessageTraceView | no | :626-627 |
DmarcTreeWalkEnabled |
1 | pre6 | ServerSettingsView | no | :639 |
SpfVoidLookupLimit |
2 | pre6 | ServerSettingsView | no | :640 |
RejectFullMailboxAtRcpt |
1 | pre6 | ServerSettingsView | no | :641 |
DkimAcceptSha1 |
0 | pre6 | ServerSettingsView | no | :642 |
QuotaWarningPercent |
90 | pre6 | ServerSettingsView | no | :643 |
ArchiveRetentionDays |
0 (never) | pre6 | ServerSettingsView | no | :644 |
MetricsPerDomainEnabled |
0 | pre6 | ServerSettingsView | no | :656 |
IndexerFullText (+ BatchSize 250, MinTokenLength 3, MaxTokensPerMessage 2048) |
0 | alpha1 | ServerSettingsView | yes | :365-390 |
MinimumFreeDiskSpaceMB |
100 | alpha1 | ServerSettingsView | yes | :417 |
DiskSpaceWarningThresholdMB |
1024 | alpha1 | ServerSettingsView | yes | :419 |
DatabaseStatementTimeout |
30 | alpha1 | ServerSettingsView | yes | :645 |
IMAPExpungeRetentionRecords |
5000 | alpha1 | ServerSettingsView | yes | :646 |
DmarcRptSchemaVersion |
1 | alpha1 | ServerSettingsView | yes (:440) | :506 |
FilterHookUrl |
"" (off) | alpha1 | ServerSettingsView | no | :658 |
FilterHookTimeoutSeconds / FailClosed / RejectScore / MaxMessageSizeKB
|
10 / 0 / 100 / 10240 | alpha1 | ServerSettingsView | no | :659-662 |
SMTPProxyProtocolEnabled / TrustedIPs
|
0 / "" | alpha1 | FeatureSettingsView | yes | :620-621 |
SMTPXClientEnabled / TrustedIPs
|
0 / "" | alpha1 | FeatureSettingsView | yes | :622-623 |
WindowsEventLogEnabled / Level
|
1 / 2 | alpha1 | FeatureSettingsView | yes | :628-629 |
OtelMetricsEndpoint / OtelLogsEndpoint
|
"" / "" | alpha1 | FeatureSettingsView | yes | :450-451 |
OtelMetricsInterval |
60 | alpha1 | FeatureSettingsView | yes | :452 |
OutboundPipelining / OutboundChunking
|
1 / 1 | 6.2.25 | (not checked) | no | :732-733 |
DeliveryHardLinks |
0 | 6.2.25 | (not checked) | no | :312 |
ArchiveDomains |
"" (all domains) | 6.2.25 | (not checked) | no | :317 |
SpamAssassinUser / SpamAssassinUserFromRecipient / SpamAssassinLearnOnMove
|
"" / 0 / 0 | 6.2.25 | (not checked) | no | :341-354 |
ScriptAllowedObjects |
* |
6.2.25 | (not checked) | no | :389 |
BackupVerifyRestore |
1 | 6.2.25 | (not checked) | no | :480 |
LogonTarpitSeconds / SmtpTarpitCount / SmtpTarpitDelaySeconds
|
0 / 0 / 0 | 6.2.25 | (not checked) | no | :675-679 |
SmtpAuthenticatedSenderCheck |
0 | 6.2.25 | (not checked) | no | :734 |
MetricsHistoryDays |
7 | 6.2.25 | (not checked) | no | :735 |
PasswordHashIterations / PasswordHashMemoryKB / PasswordHashTimeCost
|
bounded by ReadPasswordHashWorkFactor_ (10000-10000000 / 4096-1048576 / 1-20) |
6.2.25 | (not checked) | no | :233-235 |
OAuth2JwksUrl / OAuth2JwksCacheSeconds
|
"" / 3600 | 6.2.25 | (not checked) | no | :265-266 |
OAuth2IntrospectionUrl / ClientId / ClientSecret / CacheSeconds / FailOpen
|
"" / "" / "" / 300 / 0 | 6.2.25 | (not checked) | no | :271-275 |
IMAPCompressionEnabled |
1 | 6.2.28 | ServerSettingsView (Protocols > IMAP, :1111) | yes | :497 |
HttpProxy |
"" (direct) | 6.2.28 | FeatureSettingsView (Updates card, :1936) | yes | :492 in the release tree |
UpdateCheckEnabled / UpdateChannel / UpdateFeedUrl / UpdateCheckHours / UpdateTrustRootsFile / UpdateLogPublicKeyFile / UpdateSigningIdentity / UpdateSigningIssuer / UpdateSourceRepository / UpdateRequireAuthenticode / UpdateServiceWaitSeconds / UpdateWindow / UpdateAutoDownload / UpdateBackupBeforeApply
|
0 / stable / "" / 24 / "" / "" / "" / "" / "" / 0 / 180 / "" / 0 / 1 | 6.2.28 | FeatureSettingsView (Updates card, :1932) | yes | :489-509 |
Rows from 6.2.25 on were added 8 Sep 2026; their "CP view" and Ctrl+K-index presence were not re-checked except where a line is cited. Also new outside [Settings] in 6.2.25: [Database] AllowUnencryptedConnection=0, PostgreSQLSslMode="", PostgreSQLSslRootCert="", ConnectionStringOptions="" (:162-165); [Security] AdministratorTotpSecret (:112).
Pre-existing keys that only became findable in this range (6.2.14/6.2.15): LogDeleteDays, LogLevel, MaxLogLineLen, SepSvcLogs, JsonLogging, QuickRetries, QuickRetriesMinutes, QueueRandomnessMinutes, MaxOutboundPerDestinationPerMinute, SAMinTimeout/SAMaxTimeout, ClamMinTimeout/ClamMaxTimeout, IndexerFullMinutes/IndexerFullLimit/IndexerQuickLimit, UseDNSCache, DNSServer, BackupMessagesDBOnly, ArchiveDir, DisableAUTHList, PreferredHashAlgorithm, MinimumAcceptedHashAlgorithm, PasswordPepper, OAuth2*, WebServices*, Autoconfig*, MtaSts* (source: v6.2.14, v6.2.15 bodies; all present in IniFileSettings.cpp)
DB-stored (hm_settings) defaults relevant to "inert by default": enableimapacl=1, enableimapsort=1, EnableImapSASLPlain=0, MessageIndexing=0, ASArcFilteringEnabled=0 (source: DBScripts/CreateTablesMSSQL.sql:914, 918, 995, 1013, 1037)
- 6.2.18 "Correction (12 Aug 2026)": ARC sealing was narrower than described (only author-domain-signed mail); fifteen other audit defects listed in the Roadmap (source: v6.2.18 body)
- 6.2.21: the bare-LF work described in 6.2.18 was real but was not the #18 reporter's defect (Postfix normalises bare LF) (source: v6.2.21 body)
- 6.2.21: six Roadmap rows had said unimplemented Sieve constructs were silently accepted; they are refused at upload (source: v6.2.21 body)
- pre6: Roadmap row said THREAD was not implemented; it was (shipped 6.2.20); per-domain-metrics row overclaimed that every counter was global (source: pre6 body)
- alpha1:
DiagnosingStalledMail.mdtold admins the custom-DNS setting was broken; not true since 6.2.19 (source: alpha1 body) - alpha1 note 14: fourteen new settings absent from README, nine with no GUI — the nine gained README entries and CP rows on 22 Aug 2026 (source: Roadmap.md:988; grep of README.md and CP views). The five
FilterHook*keys are still absent from README (source:grep -c FilterHook README.md= 0). - Roadmap prose paragraphs are hand-counted and had drifted twice;
check-roadmap.ps1reconciles tables, not prose (source: Roadmap.md:96-116) - Release titles: 6.2.24 says "TOTP for the Control Panel" while pre3 and the code say per-account TOTP is enforced by the server (source: v6.2.24 body vs PasswordValidator.cpp:189)
-
README settings coverage: since 6.2.25 README's configuration reference says it "lists the keys an operator is most likely to need and is not the whole file" and points at the wiki Settings Reference for the other ~90 keys (README.md:379-384), so the
FilterHook*, 6.2.22-pre-line and timeout keys being absent from README is by design, not a gap (grep -c FilterHook README.mdis still 0). The 6.2.24 body's "full list ... in README.md" was inaccurate at the time (18 of 23). -
pre1 schema range: pre1 says "6012 → 6014"; the previous stable (6.2.21) was 6011 (source: pre1 vs v6.2.24 bodies;
Upgrade6011to6012*.sqlexists). - THREAD provenance: pre6 says "implemented all along"; the 6.2.20 body says it was added in 6.2.20 (source: both bodies). Both agree it is shipped and unconditional.
-
Post-quantum, LDAP auth, .NET 10, API keys, spam overview, DANE-MX fix, LOGINDISABLED, POP3 conformance: shipped in 6.2.19 by commit/tag date but absent from the 6.2.19 release body (source:
git log2026-08-12..15 vs v6.2.19 body). - 6.2.18 schema: body is silent; 6.2.19 says the move is "from 6005", so 6.2.18 was still 6005.
- Ctrl+K index size: 6.2.14 says 227 settings indexed; the generated index now has 347 entries (source: SettingsSearchIndex.g.cs).
-
IMAP SCRAM: Roadmap "ahead" list cites SCRAM-SHA-256-PLUS; over IMAP it is unreachable on a stock install because
EnableImapSASLPlain=0 gatesAUTHENTICATE(source: alpha1 limitation 22; CreateTablesMSSQL.sql:1037). -
Alpha1's
.NET 10vs 6.2.12's.NET 8: both true at their dates; the switch happened 12 Aug 2026 in the 6.2.19 window (source: commit 5409ae8d5).
- Windows Event Log curated ids
2010–2015and catch-all2000–2003; throttle "five per id per ten minutes" (only the source name, throttle function and bucket were found: WindowsEventLog.cpp:98, 255-263) - (Located since and stated in their sections on 8 Sep 2026: DKIM RSA keys under 1024 bits — DKIM.cpp:268; the IMAP 11 MB pre-authentication buffer — IMAPConnection.cpp:238; REST API keys — RestApiServer.cpp:158,2416; the Apple
.mobileconfigendpoint — WebServicesServer.cpp:653. Whether the profile first shipped in 6.2.18 or 6.2.19 is still not settled.) - ACME http-01 locator fix (#34) location in
AcmeClient.cpp - Exact DBUpdater step counts ("76 for MySQL/MSSQL, 49 for PostgreSQL/SQL CE") (alpha1 body)
-
OnClientLogonfiring from every IMAP AUTHENTICATE mechanism (6.2.13) -
TlsKeyExchangeGroupsCP/README presence (not checked) - Regression-gate counts for 6.2.17, 6.2.19, 6.2.21 (bodies say "validated by the full regression suite" without a number)
- The 6.2.25 claim that the four HTTPS clients require TLS 1.2 or later (release body only; not located by grep in HttpsClient.cpp)
hMailServer 6.3.3 · AGPL-3.0-or-later · Repository · Report a documentation error
Hmail Server — full index
Start here
1. Install and run
- Before You Install
- Installing hMailServer
- Installing on Linux
- Running in a Container
- The Control Panel
- Your First Domain and Mailbox
- Connecting a Mail Client
- DNS for Your Domain
2. Secure it
3. Operate it
- Monitoring and Health
- Backup and Restore
- Troubleshooting
- Diagnosing Stalled Mail
- Relocating an Installation
- Upgrading hMailServer
- Upgrading Guide
- Migrating the Database Backend
- High Availability Runbook
- Warm Standby
- Runbooks Digest
4. Extend it
- Rules and Sieve
- Aliases Lists and Public Folders
- Routes and Relays
- The COM API and Scripting
- The REST API
- APIs Reference
5. Contribute to it
- Project Handbook
- Architecture
- Contributing
- Release Process
- Governance
- Assurance Case
- Regression Test Environment
- Fuzzing
- Regulatory Scope
- Third-Party Binaries
Look it up — from any journey