hMailServer 6.2.22-pre3

Pre-release

Signed 19 August 2026 6.2 series

This is not the current release. hMailServer 6.3.3 is the one to install unless you need this exact version.

Downloads for 6.2.22-pre3

Windows

Windows 10 (version 1607 or later) and Windows 11, or Windows Server 2016 and newer; 64-bit only. Run the installer as an administrator: it upgrades an installation older than this version in place, and it cannot go backwards — an older server refuses a database a newer one has upgraded, so back up the database and the data directory first. It is not Authenticode-signed, so Windows names an unknown publisher and SmartScreen warns before it will run it.

  • hMailServer-6.2.22-x64.exex64 · 73.2 MB
    Download
    SHA-25624e7e6c83ffb…5fe0f014Signature: cosign bundle

What’s new in 6.2.22-pre3

A third pre-release of 6.2.22, fifteen commits on from pre2. Still a pre-release: it is gated but not yet run in anger anywhere.

Full release notes for 6.2.22-pre3 5 sections

Upgrading from pre2 requires DBUpdater — the database schema moves from 6015 to 6019 in four steps.

The theme of this one is authentication. The roadmap has said for months that per-account two-factor authentication was impossible here and named the reason; that chain is now complete, and three of the four schema steps below exist to support it. Two-factor authentication, and the chain that had to come first

  • App passwords (schema 6016). A per-account credential, revocable on its own, that authenticates over IMAP, POP3 and SMTP alongside the account's own password. It is a prerequisite rather than a convenience: a mail client has nowhere to type a code, so an account required to present one could not be opened by any client at all.
  • The secret exists once — 20 symbols from a 30-character alphabet, about 98 bits, from OpenSSL's CSPRNG with rejection sampling. Deliberately not PasswordGenerator::Generate, which returns the first twelve hex characters of a GUID: 48 bits, for a credential typed into a client once and then left for years.
  • App passwords are tried only after the account's own password has failed — checking five of them first would add half a second of Argon2id to every ordinary logon — and on a server that has never issued one, a cached existence flag answers without touching the database at all.
  • Per-account 2FA (schema 6017). TOTP has been in this product for years and protected exactly one thing: the admin tool's own logon, implemented in C# in the Control Panel and checked after the server had already accepted the password. The server had no idea the feature existed.
  • It does now. Once a secret is enrolled, the account password stops being a mailbox credential and an app password becomes the only one, which is precisely what app passwords were built to make survivable.
  • RFC 6238 with the parameters authenticator apps actually implement, and the RFC's published vectors are pinned in the server's self-tests — including the 20000000000 row, which also proves the counter stays 64-bit past 2038. The regression fixture then computes its codes with .NET's own HMACSHA1 rather than anything the server owns, because an implementation that agrees only with itself will agree with itself about the wrong answer.
  • A password policy the server actually enforces. IsStrongPassword has existed for years, carries a hard-coded deny-list, is reachable over COM — and nothing in the server has ever called it. Every mailbox on every installation could be test.
  • Five settings now decide, plus one rule that is always on: a password may not contain the account name. Enforced where a password is chosen and nowhere that verifies or re-hashes an existing one, because a policy applied to verification locks people out of mailboxes they can open today.
  • Password expiry and history (schema 6019). Shipped as a pair, because expiry without history teaches people to alternate between two passwords — worse than not expiring at all.
  • Expiry is the half with a caveat, and it is written into the setting itself: this server has no self-service password change, so an expired password can only be reset by an administrator. Existing app passwords keep working, Active Directory accounts are exempt, and an unreadable stamp is never treated as expired — the failure that would otherwise lock out every account on any server that upgrades.

Anti-spam

  • Admin-reviewable quarantine (schema 6018). Messages the server would otherwise have refused are held in a store an administrator can list, read, release or delete, with a review queue in the Control Panel.
  • The design turns on one fact: refusing happens during the SMTP conversation with a 550, so the message is never accepted and there is nothing to review.
  • Quarantining answers 250 — the sender believes it was delivered and will not retry, which is what makes a false positive recoverable without bouncing to a return path that is probably forged, and also means this store is the only place that message now exists.
  • Post-transmission verdicts only, because a verdict reached before DATA has no message to hold. A quarantine that fails to store falls through to refusing: accepting mail that was not actually stored would turn a spam refusal into silent deletion. Releasing re-enters at the delivery queue rather than at SMTP, so the filters that quarantined it cannot undo the administrator's decision. Off by default.

IMAP and POP3

  • FETCH BINARY on a composite section returned an empty literal — the whole message, a multipart part and a message/rfc822 part all came back as nothing, and BINARY.SIZE agreed with them, so the answer was internally consistent and completely wrong. Shipped as a documented limitation in pre2 and fixed here. BINARY now also announces content with literal8 (~{n}) as RFC 3516 requires, because decoded bytes may contain NUL and a plain literal cannot carry one.
  • POP3 EXPIRE and LOGIN-DELAY (RFC 2449). EXPIRE is answered NEVER, which is this server's actual retention policy rather than a placeholder.
  • LOGIN-DELAY declares and enforces a minimum interval between logins, refusing an early one with -ERR [LOGIN-DELAY] — the code a conforming client backs off from, rather than a reply indistinguishable from a wrong password.
  • Off by default.

OAuth2

  • ES256 tokens now verify. The obstacle was a format mismatch rather than a missing algorithm: JWS carries an ECDSA signature as the raw R||S pair while OpenSSL verifies X9.62 DER. The transcode is pinned against OpenSSL's own encoder, case by case, because one checked by round-tripping through its own decoder proves only that it agrees with itself.
  • The configured public key is now also required to be the kind the algorithm names — an EC key against RS256, or RSA against ES256, is refused on the key type rather than surfacing as a signature failure. And implementing an algorithm does not enable it: a valid ES256 token is still refused while the allow-list omits it.

The test suite stopped answering to somebody else's DNS

Not a server change, but the reason four gate runs failed for reasons unrelated to the code under test, and worth recording because it cost most of a night.

Ten tests resolved live third-party names. Each red gate exposed a fresh set — six, then one, then two, then four — and the timing was the tell rather than anything in the messages: three DKIM verification tests took 24-35 seconds each instead of one, because a DKIM key that cannot be fetched is a temporary error, so the message is accepted and a test asserting it is rejected fails while reporting nothing whatsoever about DKIM.

Ten are now served from a local zone. In three cases the assertions got stronger rather than merely faster: two fixtures used to compare the server's answer against a lookup the test process made of the same name — two live lookups that merely had to agree with each other — and now check a value the fixture chose.

A separate defect underneath cost 653 failures in one run: an error provoked by a test that has already failed goes on to fail every test after it. Teardown now prints the ERROR log with the failure that caused it and clears it until it stays clear, and drains the delivery queue as well — a queued message is a delivery that has not happened yet, and the error it eventually raises belongs to a test that has not started.

The resolver turned out to be the real answer. Making a fast resolver the machine's primary took the suite straight to green; as a secondary it made things worse, because Windows falls through only when the first server does not answer at all, never merely because it is slow.

Provenance

Gated at 1700/1700 on the stamped binary (build 28, commit 5f7098f), with 630/630 Control Panel tests. SBOMs and cosign signatures are attached by CI after publication; the installer is smoke-tested by CI against this tag. The installer is deliberately not run on the build machine.

Release facts

Version
6.2.22-pre3pre-release
Signed
19 August 2026Sigstore transparency log
Packages
1Windows, x86-64
Database schema
6019upgrades in place
Installer SHA-256
24e7e6c83ffb…5fe0f014hMailServer-6.2.22-x64.exe

Every file in 6.2.22-pre3

Full SHA-256 of each file as recomputed on this server, with its Sigstore bundle beside it. The SBOM is not needed to install; it lists every component in the build.

FileWhat it isSizeSHA-256Signature
hMailServer-6.2.22-x64.exeWindows installer · x6473.2 MB24e7e6c83ffbbd852703d629257418c756072053acd2d7c7ee5ff4995fe0f014Signature: cosign bundle
hmailserver.spdx.jsonSBOM (SPDX)Every component in the build, in SPDX JSON.148 KB027d9005c57116aca92ef7b648fb63987b4111553da151b2f5c394b1cb17e435
hmailserver.cyclonedx.jsonSBOM (CycloneDX)The same inventory in CycloneDX JSON.88 KBf4cf722aaac5c21b5e682fcb18f3c2064a08b0daea5b0a1e1133453d5e767966