Skip to content

Pre-release: hMailServer 6.2.22-pre2 (build 27)

Pre-release
Pre-release

Choose a tag to compare

@RainbowWizard-coder RainbowWizard-coder released this 18 Oct 05:18
· 3118 commits to master since this release

Re-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 with rua=. Inert until
    DmarcRptFromAddress is set.
    RFC 7489 §7.1's external-destination verification is enforced: a rua target
    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 a rua naming 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, when TlsRptFromAddress is
    unset, because the send pass pops each day destructively and a diagnostic must
    not destroy the data it was asked to show. SendDmarcReports is 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. AccountLockoutThreshold counts 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[…] and BINARY.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. APPEND accepts the literal8 (~{n}) form.
  • OBJECTID (RFC 8474) — EMAILID follows a message through copies, so a
    client can recognise mail it has already downloaded when it reappears in
    another folder; MAILBOXID survives RENAME. THREADID is 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 /Test no 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 bare throw; — 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 BINARY on a composite section returns an empty literal. The whole
    message (BINARY[]), a section naming a multipart part, and a section naming a
    message/rfc822 part 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=None or a missing p= 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 FetchOAuth2Hosts said "empty = none" and
    rendered blank while the server defaults it to outlook.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).