Repository navigation
Pre-release: hMailServer 6.2.22-pre2 (build 27)
Pre-releaseRe-published on GitHub on 18 October 2026 from the GitLab release v6.2.22-pre2 of 2026-08-18. Each file below was checked against the SHA-256 that GitLab's package registry records for it before it was uploaded here.
Re-published on GitLab on 24 September 2026 from the GitHub release of 18 August 2026 (2026-08-18 01:46:35 UTC). Issue, pull request and discussion numbers below are GitHub's.
6.2.22-pre2 (build 27)
A second pre-release of 6.2.22, fifteen commits on from pre1. Still a
pre-release: it is gated but not yet run in anger anywhere.
Upgrading from pre1 requires DBUpdater — the database schema moves to 6015.
Reporting: this server now sends the reports it has always asked others for
- DMARC aggregate reports (rua, RFC 7489). hMailServer has consumed DMARC
policy for years and produced nothing for the domains whose mail it receives.
It now aggregates every evaluation per UTC day and per policy domain — passes
included, because a report carrying only failures tells a domain owner nothing
about whether their legitimate mail aligns — and mails the Appendix C XML to
the addresses a domain asks for withrua=. Inert until
DmarcRptFromAddressis set.
RFC 7489 §7.1's external-destination verification is enforced: aruatarget
outside the policy domain's own organizational domain is only used when
<policy-domain>._report._dmarc.<target-domain>says it wants the reports.
Without that check, anyone could publish aruanaming somebody else's
mailbox and point every reporter on the internet at it.
Forensic (ruf) reports are deliberately not implemented: a failure
report carries the whole message, and the large receivers stopped sending them
years ago for that reason. - TLS-RPT reporting became verifiable. The machinery existed, but the task
only mails days that are over, so an administrator who had just configured
it had nothing to look at until tomorrow — and nothing could test it.
Utilities.SendTlsRptReports(IncludeCurrentDay)sends what has been collected
now. It refuses, with the statistics preserved, whenTlsRptFromAddressis
unset, because the send pass pops each day destructively and a diagnostic must
not destroy the data it was asked to show.SendDmarcReportsis its twin.
Authentication
- Per-account lockout. The per-IP auto-ban cannot see a distributed attack:
a botnet spending one guess per address per account never crosses any single
address's threshold.AccountLockoutThresholdcounts by the name being
guessed at — the one thing such an attack cannot vary — and locks it. It is
enforced on every path a password can be guessed through, SCRAM included, and
every mechanism that authenticates clears the counters on success. Off by
default.
A locked name is refused with the ordinary invalid-credentials reply, and that
refusal is deliberately kept out of the per-IP auto-ban: the person most
likely to retry a locked name is its owner, with the correct password, and
charging those attempts would ban an innocent office NAT for everyone behind
it. - POP3 AUTH-RESP-CODE (RFC 3206). Credential failures now carry
[AUTH]and
a failed inbox load carries[SYS/TEMP], so a client can tell "your password
is wrong" (reprompt) from "the server is having trouble" (retry quietly).
Without it, a transient database problem made scheduled clients nag their
users for a password that was never wrong. - POP3 PIPELINING and IMPLEMENTATION (RFC 2449). PIPELINING was advertised
only after batched commands were proved to work across single TCP segments and
after confirming the STLS injection defence still holds; IMPLEMENTATION
answers the product name and deliberately no version. - APOP and the CRAM-MD5 family are now documented, at the code, as decisions
rather than a backlog: each needs a cleartext-equivalent stored secret that
this server's Argon2id/SCRAM store rightly cannot produce.
Anti-virus
- A scan that could not run is no longer the same as a clean message.
Previously such a message was delivered with one line in the error log, which
is indistinguishable from having been scanned and found clean.AVFailAction
names that posture:0(the default) keeps today's behaviour exactly,1
holds the message, re-attempts it, and returns it to the sender rather than
ever delivering it unscanned.
IMAP
- BINARY (RFC 3516) —
FETCH BINARY[…],BINARY.PEEK[…]andBINARY.SIZE[…]
decode a part's Content-Transfer-Encoding server-side, so a client saving an
attachment stops downloading base64 it must immediately decode itself. Partial
ranges apply to the decoded bytes.APPENDaccepts the literal8 (~{n}) form. - OBJECTID (RFC 8474) —
EMAILIDfollows a message through copies, so a
client can recognise mail it has already downloaded when it reappears in
another folder;MAILBOXIDsurvivesRENAME.THREADIDis answered NIL,
which the RFC provides for, rather than inventing one. Schema 6015.
Fixes
- An idle server now stops in about half a second rather than up to two: the
work-queue join retried on a 250ms interval after a 1ms wait, so whether a
shutdown cost nothing or two seconds turned on scheduler timing the server
does not control. hMailServer.exe /Testno longer terminates on an unhandled exception when
the machine has no network, or when a third party's DNS breaks: the SPF
self-test evaluated a live policy for a domain that now answers SERVFAIL, and
signalled failure with a barethrow;— which outside a catch block calls
std::terminate.
Control Panel
- Nine settings the server acts on had no field at all and were reachable only
by hand-editing the ini: the virus-scanner failure policy, the per-name
lockout, DMARC report settings, and the POP3 fetcher's OAuth2 host list. Each
now sits on the page where the question is asked — the scanner policy beside
the scanners, the lockout beside auto-ban — and carries the caveat, not just a
label.
Known limitations in this pre-release
FETCH BINARYon a composite section returns an empty literal. The whole
message (BINARY[]), a section naming a multipart part, and a section naming a
message/rfc822part all come back empty, because the decode path serves the
entity's raw text and a multipart entity's text stops at the first boundary.
Leaf parts — the attachment case BINARY exists for, and the one the tests
cover — are correct. Found by the pre-release review; the fix belongs with the
partial-fetch code rather than in a release cut.- DMARC report data lives in memory, so a restart before the daily send loses
that day's statistics, and a resolver outage during the hourly run discards
the day it was about to report. Aggregate reporting is best-effort
observability, not a ledger.
What the pre-release review changed
The diff since pre1 was reviewed adversarially before this was cut, and three of
the four things it found were in code or text written in the previous two days:
- DMARC reports could be schema-invalid —
p=Noneor a missingp=reached
the XML verbatim, and RFC 7489 types those as a closed lowercase enumeration,
so validating receivers discarded the whole report while the log said it was
sent. Policy tags are now mapped onto the enumerations. - Report ids collided: the id (and the Message-ID built from it) was day +
second + submitter with no mention of the domain being reported on, so two
domains reported in the same second shared them. Fixed in both reporters. - The new Control Panel field for
FetchOAuth2Hostssaid "empty = none" and
rendered blank while the server defaults it tooutlook.office365.com—
and saving that page would have written the blank back, silently stopping
Microsoft 365 fetching. Both OAuth2 host fields now show the real default.
Provenance
Built from 6f01f5f, gated at 1650/1650 on the stamped binary, 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.
Assets
| File | Size | SHA-256 |
|---|---|---|
hMailServer-6.2.22-x64.exe |
76,750,637 | 08b1d7a45a840eea648365cd979924dcc5bbad0b8d0427f10969b013e2bcd9f7 |
hMailServer-6.2.22-x64.exe.cosign.bundle |
10,907 | 7f0a4193108ded9d9d20e9c4264b8b4485fa31aa850526e483183ec06857b826 |
hmailserver.cyclonedx.json |
89,889 | 4f69ce616cacd61e420e8ad50eea0670ba8352469401be59a1b5d76b4f2739d2 |
hmailserver.cyclonedx.json.cosign.bundle |
10,955 | 9d2074923f52f12477f75c0922026650909319f5aa9f24552caad01c1f5e6a6b |
hmailserver.spdx.json |
151,498 | 8154aa4ad8b52c97ab4caa03f9f00a020d84fd06948d3eee5aaeacee15c4212f |
hmailserver.spdx.json.cosign.bundle |
10,667 | 14e14506bc9db351b53fda45f7aceb2bc38d21d87105d29060b38d6021a4c6c5 |
These are the files published with the GitHub release, byte for byte, re-hosted in this project's package registry. The hashes above were computed from them on 24 September 2026. Each .cosign.bundle / .sigstore.json is the Sigstore bundle GitHub Actions made when the release was signed; all of them were verified again before re-publishing, against the former GitHub repository's workflow identity (https://github.com/Progressiverobot/hmailserver/..., issuer https://token.actions.githubusercontent.com), which is the identity to give cosign verify-blob for these files. The Windows installer of this release was not Authenticode-signed (Authenticode signing began with 6.3.1).