Skip to content

Pre-release: hMailServer 6.2.22-pre6 (build 30) — Pre-Alpha

Pre-release
Pre-release

Choose a tag to compare

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

Re-published on GitHub on 18 October 2026 from the GitLab release v6.2.22-pre6 of 2026-08-20. 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 20 August 2026 (2026-08-20 23:09:03 UTC). Issue, pull request and discussion numbers below are GitHub's.

6.2.22-pre6 (build 30) — Pre-Alpha

Same build as pre5, republished with its supply-chain assets. pre5 went out
carrying only the installer: this repository has immutable releases enabled, so
the SBOM and signing workflows could not attach anything after publication. That
is fixed — assets are now attached while the release is still a draft — but a tag
used by an immutable release can never be reused, so the corrected build had to
come back under a new one. The code is identical to pre5 (build 30, commit
bf0d38a); what is new is the SPDX and CycloneDX SBOMs and a cosign bundle for
every asset, the installer included.

This is pre-alpha. It is gated — 1721 regression tests, nothing skipped — and
the installer is smoke-tested on a throwaway runner, but it has not been run in
anger on a production server by anyone. Treat it as something to try on a spare
box, not as an upgrade for a mail server people depend on.

pre4 was withdrawn. Its installer could not create its own database: two
SQL statements were written on consecutive lines, and the script parser splits
commands on a blank line, so SQL Server Compact — what a default install uses —
rejected the pair as one command. The install succeeded, the service started,
and it listened on nothing. Nothing local caught it, because this project's test
database is upgraded out of band and no test had ever taken the create path a
fresh install takes; the installer smoke test found it after publication. There
is now a check-db-scripts step that builds a throwaway database from the real
create script, and it is part of the release checklist. The same defect was
found and fixed in the 6020 upgrade step, which only ever shipped in pre4.
The v6.2.22-pre4 tag still exists (tag deletion is blocked by a repository
rule) but has no release attached; do not build from it.

Sixteen commits on from pre3.

Upgrading from pre3 requires DBUpdater — the schema moves from 6019 to
6022 in three steps.

The theme is standards catching up with reality: DMARCbis and the RFCs around it
were published while this fork was being written, certificate lifetimes are
falling on a schedule, and several things this server did were correct when they
were written and are not any more.

DMARC, brought up to DMARCbis

  • The DNS tree walk (RFC 9989 §4.10) replaces the Public Suffix List for
    deciding whether two names belong to the same organization — which is what
    DMARC's default relaxed alignment compares. This is not tidying: where a
    sender relies on tree-walk semantics, a list-based answer is a wrong policy
    decision
    rather than a soft failure, in both directions. The list is kept as
    the DmarcTreeWalkEnabled=0 escape hatch and as the answer whenever a
    lookup fails transiently — a resolver outage must not silently turn relaxed
    alignment into strict.
    Bounded at eight queries per domain and cached for five minutes, because DMARC
    needs an organizational domain for the author domain and every identifier
    aligned against it: uncached, one message could cost two dozen lookups.
  • The np= tag (RFC 9989 §5.5.4) — the policy for subdomains that do not
    exist. p= covers the domain and sp= its subdomains, and between them they
    left the cheapest forgery there is uncovered: a phisher does not need a
    subdomain delegated to them, only a From header that says
    accounts.thebank.test, and no domain owner can publish a record for a name
    they have never heard of.
    The whole tag turns on NXDOMAIN versus NODATA, so the resolver now reports the
    RCODE. A subdomain holding only an MX or a TXT record exists, and treating
    those as absent would apply np=reject to real parts of a sender's own estate.
  • Aggregate reports name the selector. RFC 7489 has a <selector> inside
    auth_results/dkim and it was never emitted, so a domain owner reading their
    own report could see that something of theirs signed a message but not which
    key
    — the one thing they need during a rotation or after a compromise. The
    SPF <scope> is now emitted for the same reason.

Algorithms that stopped being safe

  • DKIM rsa-sha1 is refused (RFC 8301). This server both signed and
    verified with it. A DKIM signature is an assertion of identity, DMARC
    alignment is built on it, and SHA-1 chosen-prefix collisions have been
    practical since 2020 — a forgeable signature is worse than an absent one,
    because it carries the signer's domain past an aligned DMARC check. A domain
    configured to sign with it is now signed rsa-sha256 instead and told so.
    DkimAcceptSha1=1 restores both halves for a correspondent who has not moved.
  • DKIM keys below 1024 bits are refused (RFC 8301 §3.2). Nothing checked, so
    a domain publishing a 512-bit key — several did for years, because a smaller
    key fits more comfortably in a TXT record — had its signatures verified as
    normal. A 512-bit key is factorable in hours on rented hardware.
  • SPF void-lookup limit (RFC 7208 §4.6.4). The ten-term limit bounds what a
    policy can demand; it does not bound what a policy can waste. Ten terms
    naming hosts that do not exist cost a full resolution each for a record that
    can never match. Capped at two, SpfVoidLookupLimit to change or disable.

Certificates, before the lifetimes drop

  • The ACME renewal window is now a fraction of the certificate's lifetime.
    Thirty days was right while certificates lasted ninety and stops being right
    the moment they do not — Let's Encrypt defaults to 64 days from February 2027,
    the maximum falls to 100 days in March 2027 and to 47 in March 2029. A
    fixed 30-day window against a 47-day certificate starts renewing seventeen
    days after issuance and never stops, which is how an operator gets
    rate-limited by their own CA. Renewal now begins two thirds through whatever
    lifetime the certificate has, with a one-day floor.
  • ARI (RFC 9773). The CA's renewalInfo endpoint is consulted hourly and
    its suggested window wins when offered, so a CA can pull renewals forward
    during a mass revocation. A point is chosen inside the window rather than at
    its start — renewing at start moves the thundering herd rather than removing
    it — derived from the certificate's own identifier so it is stable across the
    hourly re-checks. Whatever the CA says is clamped to a day before expiry: ARI
    is advisory and the certificate is not. A CA without ARI, a failed request or
    an unreadable answer all mean "decide for yourself", never "do not renew".

Mail that used to be accepted and then bounced

  • A full mailbox is refused during the SMTP conversation with 452 4.2.2.
    Quotas were checked only at delivery, so a message for an over-quota mailbox
    was accepted and then answered with a non-delivery report addressed to the
    envelope sender — which the traffic that fills mailboxes fastest has forged.
    That report went from this server to somebody who sent nothing: backscatter,
    with this server's reputation paying for the forgery. Temporary rather than
    permanent, so a legitimate sender's mail waits in its queue.
    RejectFullMailboxAtRcpt=0 restores the old behaviour.
  • Quota warnings (schema 6022). Nothing told an account holder their mailbox
    was filling up, so the first they heard of it was mail no longer arriving.
    QuotaWarningPercent (default 90, 0 disables) sends one notice — crossing
    a threshold is an event, not a state, so nothing has to be remembered and
    emptying the mailbox and filling it again warns again.

Asked for

  • Per-domain outbound relay (schema 6021) — GitHub discussion 31. Each domain
    can have its own relay host, port, credentials and transport security, matched
    by the sending domain. Deliberately a different question from a route: a
    route is about where mail is going, this is about where it leaves from.
    Both can apply, so the order is fixed and tested — route, then the sending
    domain's relay, then the server-wide relayer. An empty relay host means the
    domain has no opinion, which is what every existing domain has.

Operations

  • A queryable message trace (schema 6020). "What happened to the message
    Jane sent at 14:20" used to mean grepping several log files and hoping the
    relevant one had not rotated. Off by default, because it records who
    corresponds with whom.
  • Archive retention. ArchiveDir is a raw copy of every message that passes
    through the server and nothing ever removed anything from it. ArchiveRetentionDays
    prunes it; 0 by default, because an archive is usually kept for a legal
    reason and a server that started deleting from one on upgrade would be
    destroying what it was told to keep. Only ever removes .eml files, and does
    not follow symlinks or junctions.
  • Per-domain metric labels. Every counter was server-wide, which is what
    stopped metrics becoming reporting. MetricsPerDomainEnabled adds a domain=
    label to message counters. Off by default — every label value is a separate
    time series — and the label set is bounded by construction: only domains
    this server hosts are ever labelled, so no amount of mail from strangers can
    create series.

Corrections to the record

  • THREAD (RFC 5256) was listed as not implemented and has been implemented all
    along
    — both algorithms, the capability, UID THREAD, thirteen tests. The
    roadmap row was written from an assumption and is corrected rather than the
    work repeated.
  • The per-domain metrics row claimed every counter was global; protocol=,
    queue= and build-info labels already existed.

Known limitations

  • The DMARCbis 2.0 aggregate-report namespace (RFC 9990) is not implemented;
    reports are still the RFC 7489 form, which every consumer understands.
  • Per-account metric labels are not implemented and are not planned in this
    shape: accounts are unbounded in a way domains are not.
  • Archive retention has no per-domain scope, index, immutability or legal hold.
  • This release has not been installed by the maintainer on a production
    server. The installer is smoke-tested on a throwaway CI runner only.

Assets

File Size SHA-256
Progressiverobot-hmailserver-attestation-42002219.sigstore.json 3,933 29af2ae1d3b84209484918113a71584508ed2df579897c2d8e4cfbce8f60b343
hMailServer-6.2.22-x64.exe 76,834,030 eb565d4b604b64c78e513a97e48e54716f2a059107990badc7d16b98b384cdcd
hMailServer-6.2.22-x64.exe.cosign.bundle 10,567 59df5adb5e49fa117d2ea482cbff0fb4c57d8430d0b4dea976e975b70eb2d012
hmailserver.cyclonedx.json 98,021 52edf8838e670be0eb59ff34055276e09a0bbf608031ac956d08a43bdfe18928
hmailserver.cyclonedx.json.cosign.bundle 10,671 29fb6cf0aedeb75ad7e3c4f63a08e6e53f28d8f67d68473f2f24a285bd1ad20e
hmailserver.spdx.json 163,668 6f2f2b774cc6b2affa11e33505d7a96f86dde57b5ed56d48c1eb44abb5c60f3b
hmailserver.spdx.json.cosign.bundle 10,715 15e3471a48889c1fe1a834600caab95fabd0281dddac2d89d5662e41d032d987

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).