Skip to content

Project Handbook

chrisholloway5 edited this page Sep 11, 2026 · 5 revisions

Project Handbook

Building each component, running the tests, the contribution rules and required checks, the release process, verifying a release, the security policy, governance, the assurance case, the licence and the third-party components. File and line references point into the repository at the commit this page was written from (6.2.24, master of 4 September 2026). Sections headed Unconfirmed or Contradictions record what could not be verified or where documents disagreed, and are left in on purpose. The build graph, the pre-flight table and the CI diagrams were added on 8 September 2026 and drawn from build/, build/preflight-tests.ps1 and .github/workflows/ as they stand in 6.2.28.

Verified 2026-09-04 against the working tree at f13ac06fc (branch server-fixes-wave, v6.2.24-5-gf13ac06fc) and the GitHub repository Progressiverobot/hmailserver via gh api. Paths are relative to the repository root unless stated. "(doc)" means the fact is what a document says and was not independently checkable from code; "(code)" means it was grepped/parsed; "(gh)" means it came from the GitHub API.

1. Identity and versions

  • Product: hMailServer, a fork of the original Windows mail server (SMTP/IMAP/POP3), maintained by Christopher Holloway / Progressive Robot Ltd (source: README.md:7-9)
  • Repository: https://github.com/Progressiverobot/hmailserver (source: GOVERNANCE.md:5)
  • Current version 6.2.28, build 38: HMAILSERVER_VERSION "6.2.28", HMAILSERVER_VERSION_NUMERIC 6,2,28,38, HMAILSERVER_BUILD 38 (source: hmailserver/source/Server/Common/Application/Version.h:5-7, code)
  • The same version is stamped in exactly seven .csproj <Version> values: ControlPanel, DataDirectorySynchronizer, DBSetup, DBSetupQuick, DBUpdater, ImportTool, Shared (source: grep -n "<Version>" hmailserver/source/Tools/*/*.csproj, code; RELEASE.md:48-49 says "all seven")
  • Installer stamp: AppVersion=6.2.28, OutputBaseFilename=hMailServer-6.2.28-x64, VersionInfoVersion=6.2.28.0 (the fourth component stays 0 on purpose) (source: hmailserver/installation/section_setup_64.iss:2,6,10, code)
  • Schema: REQUIRED_DB_VERSION 6031 (source: hmailserver/source/Server/Common/Application/Constants.h:173, code). README.md:110 still says "Database version 6027" - stale, see section 17.
  • Latest published release: v6.2.28, published 2026-09-08T07:31:17Z, not draft, not prerelease, six assets: hMailServer-6.2.28-x64.exe, hmailserver.spdx.json, hmailserver.cyclonedx.json, each with a .cosign.bundle (source: gh release view v6.2.28 --json assets, gh). v6.2.27 was published on 2026-09-07, v6.2.25 and v6.2.26 on 2026-09-06.
  • Tags present: v6.2.19 ... v6.2.22-pre1..pre6, v6.2.23-alpha1, v6.2.23-alpha2, v6.2.24, v6.2.25, v6.2.26, v6.2.27, v6.2.28 (source: git tag -l 'v*', code)
  • v6.2.23-alpha1 is a lightweight tag (git cat-file -t = commit); v6.2.23-alpha2 and every tag since (v6.2.24 to v6.2.28) are annotated tag objects (source: git cat-file -t refs/tags/<tag>, code; matches SECURITY.md "from v6.2.23-alpha2 on")
  • Badges: OpenSSF Best Practices project 14187 and OpenSSF Scorecard (source: README.md:4-5). Best Practices level is "Passing" at 100% (Basics 13/13, Change Control 9/9, Reporting 8/8, Quality 13/13, Security 16/16, Analysis 8/8) (source: https://www.bestpractices.dev/projects/14187, fetched 2026-09-04)
  • Supported platforms: 64-bit Windows only, Windows 10 1607 / Server 2016 (build 14393) upward, enforced by the installer (source: README.md:127-129, doc)

2. Building: toolchain and prerequisites

  • Compiler: Visual Studio 2026, platform toolset v145, x64 only (source: hmailserver/source/Server/hMailServer/hMailServer.vcxproj:22,29 <PlatformToolset>v145</PlatformToolset>, code; README.md:97; .github/CONTRIBUTING.md:12)
  • MSVC version: the tree pins the toolset family only - the native-dependencies action requires a 14.5* MSVC directory ("The v145 (14.5x) toolset is not installed") (source: .github/actions/native-dependencies/action.yml:77-82, code); the v6.2.24 release notes named the machine's compiler as MSVC 14.51.
  • Windows SDK: the project pins only <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> (latest installed) (source: hMailServer.vcxproj:17, code). RELEASE.md:76 and the v6.2.24 notes name "Windows SDK 10.0.26100" as the toolchain used - that is the machine's SDK, not a project pin.
  • VS workloads required: ".NET desktop development" (or "Managed Desktop Build Tools"), "Desktop development with C++"; components "C++ ATL for latest v145 build tools (x86 & x64)" and a Windows 10/11 SDK (source: README.md:195-199, doc)
  • winget one-liner: winget install Microsoft.VisualStudio.BuildTools --override "--quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools;includeRecommended --add Microsoft.VisualStudio.Component.VC.ATL --add Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools" (source: README.md:203-205, doc)
  • Other required software: Inno Setup 6 (winget install JRSoftware.InnoSetup, installer only), Perl 5 / Strawberry (OpenSSL only), Python 3 (libpq only) (source: README.md:180-185, doc). README.md:180 also lists "An installed version of hMailServer 5.7 or later (configured with a database)" - inherited upstream text.
  • MSBuild is located with vswhere (-latest -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe) (source: build/Find-MsBuild.ps1:10-19, code)
  • Native libraries live OUTSIDE the tree under the hMailServerLibs environment variable; the project references $(hMailServerLibs)\boost_1_92_0, $(hMailServerLibs)\openssl-4.0.2, $(hMailServerLibs)\postgresql-18.3 (source: hMailServer.vcxproj:76,100, code; post-build.bat:12-18). build/Get-LibraryVersions.ps1 parses those paths back out (openssl=4.0.2, boost=1.92.0, postgresql=18.3) for the CI cache keys.
  • Scripted route: libraries\build-openssl.ps1 -Version 4.0.2, libraries\build-boost.ps1 -Version 1.92.0 and libraries\build-pgsql.ps1 each download one source archive, verify it against a SHA-256 pinned in the script and build it under %hMailServerLibs% (source: README.md:287-289; libraries/build-openssl.ps1:54-94, code)
  • OpenSSL 4.0.x build: Perl Configure no-asm VC-WIN64A --prefix=%cd%\out64 --openssldir=%cd%\out64 -D_WIN32_WINNT=0x0A00, nmake clean, nmake build_libs, nmake install_dev install_runtime_libs (not install_sw) (source: README.md, doc). Runtime DLLs copied are libcrypto-4-x64.dll and libssl-4-x64.dll (source: hmailserver/source/Server/hMailServer/post-build.bat:12-15, code)
  • PostgreSQL 18.3 libpq build: winflexbison on PATH, py -m pip install meson ninja, then meson setup builddir --buildtype=release -Dssl=openssl -Dauto_features=disabled -Dextra_include_dirs=%hMailServerLibs%\openssl-4.0.2\out64\include -Dextra_lib_dirs=%hMailServerLibs%\openssl-4.0.2\out64\lib and meson compile -C builddir src/interfaces/libpq/libpq:shared_library (source: README.md:325, doc). Output copied from postgresql-18.3\builddir\src\interfaces\libpq\*.dll (source: post-build.bat:18, code)
  • Boost 1.92.0 build: bootstrap then b2 debug release threading=multi link=static --with-thread --with-filesystem --with-regex --with-chrono --with-atomic address-model=64 stage --build-dir=out64 -j 4 define=BOOST_USE_WINAPI_VERSION=0x0A00 (source: README.md:335-336, doc)
  • libraries/build-openssl.ps1, build-boost.ps1 and build-pgsql.ps1 (sharing build-common.ps1) each download one source archive from the project's own release server, verify it against a SHA-256 pinned in the script, and build it outside the tree under %hMailServerLibs%; nothing they fetch is committed (source: libraries/build-openssl.ps1:54-94; hmailserver/docs/ThirdPartyBinaries.md:48-54, code)
  • .NET SDK pinned to 10.0.303, rollForward latestFeature, allowPrerelease false (source: global.json:3-6, code)
  • All ten .csproj under source/Tools target net10.0-windows (ControlPanel, ControlPanel.Core, ControlPanel.Tests, DataDirectorySynchronizer, DBSetup, DBSetupQuick, DBUpdater, ImportTool, ImportTool.Tests, Shared) (source: grep TargetFramework hmailserver/source/Tools/*/*.csproj, code)
  • Control Panel assembly name is hMailCP (WPF, WinExe) (source: hmailserver/source/Tools/ControlPanel/ControlPanel.csproj:4-15, code)
  • NuGet lock files are enforced: RestorePackagesWithLockFile=true and EnableWindowsTargeting=true for every Tools project (source: hmailserver/source/Tools/Directory.Build.props:17,33, code); CI restores with --locked-mode (source: .github/workflows/ci.yml:65,75,113)
  • The regression suite targets .NET Framework 4.8.1 (<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>) with NUnit 4.6.1, NUnit.ConsoleRunner 3.22.0, NUnit3TestAdapter 6.3.0 (source: hmailserver/test/RegressionTests/RegressionTests.csproj:38; hmailserver/test/RegressionTests/packages.config:31-33, code)
  • The .NET tools build against a checked-in TlbImp wrapper hmailserver/source/Tools/Interop/Interop.hMailServer.dll, so no registered typelib is needed for dotnet build (source: hmailserver/source/Tools/Interop/README.md:3-7; ci.yml:6-7)
  • Building on a machine with a production hMailServer: pass /p:PreBuildEventUseInBuild=false /p:PostBuildEventUseInBuild=false - build.ps1 already does (source: README.md:295; build/build.ps1:44-45, code)
  • One build tree; the service holds the output binary, so stop the service before linking and never build during a regression run (source: ARCHITECTURE.md "Building")

3. Building: commands per component

  • Server: build\build.ps1 -Configuration Release (default is Debug; -Clean runs /t:Clean first). Runs MSBuild on hmailserver\source\Server\hMailServer\hMailServer.sln with /m /p:Configuration=<cfg> /p:Platform=x64 /p:PreBuildEventUseInBuild=false /p:PostBuildEventUseInBuild=false, log to logs\build-<cfg>.log (source: build/build.ps1:4-66, code). -Asserts (added 5 Sep 2026, Release only) passes /p:KeepAssertions=true, which defines HM_KEEP_ASSERTIONS: the server's own HM_ASSERT macro (StdAfx.h; three modes - Debug = CRT assert, Release = compiled out, kept = reported) then reports a violated assertion as Critical HM6364 through ErrorManager naming expression, file and line, and carries on; log logs\build-Release-asserts.log; the dynamic-analysis build, never the one that ships. Diagnostics.AssertionsEnabled says which binary is running and Diagnostics.TriggerAssertion() provokes one (source: build/build.ps1; hMailServer.vcxproj KeepAssertions group; StdAfx.h; COM/InterfaceDiagnostics.cpp)
  • Server post-build (elevates via UAC): build\post-build.ps1 -Configuration Release - runs post-build.bat <hMailServerLibs> <outDir> <exe> <intDir>, which does NET STOP hMailServer, copies OpenSSL, libpq, MariaDB Connector/C (libraries\mariadb-connector-c-3.4.9\libmysql.dll + plugin\*.dll) and hMailServer.tlb beside the exe, then hMailServer.exe /Register (source: build/post-build.ps1:8-32; hmailserver/source/Server/hMailServer/post-build.bat:10-42, code)
  • Output location: hmailserver\source\Server\hMailServer\x64\<Configuration>\hMailServer.exe (source: build/post-build.ps1:16-17, code)
  • Admin/setup tools: build\build-tools.ps1 (default Release) runs dotnet publish <proj>.csproj -c <cfg> -o <proj>\publish for DBSetup, DBSetupQuick, DBUpdater, DataDirectorySynchronizer, ImportTool, deleting each publish folder first (source: build/build-tools.ps1:21-34, code). Solution: hmailserver/source/Tools/hMailServer Tools.sln (projects Shared, DBSetup, DBSetupQuick, DBUpdater, DataDirectorySynchronizer, ImportTool) (source: grep Project( "hMailServer Tools.sln", code)
  • Control Panel: NOT covered by build-tools.ps1; it is a separate dotnet publish of hmailserver\source\Tools\ControlPanel\ControlPanel.csproj into its publish\ folder (source: build/build-tools.ps1:7-11; RELEASE.md:52-55). Solution hmailserver/source/Tools/ControlPanel.sln holds ControlPanel, ControlPanel.Core, ControlPanel.Tests (source: grep Project( ControlPanel.sln, code). CI's publish command is dotnet publish hmailserver/source/Tools/ControlPanel/ControlPanel.csproj -c Release -o publish --no-build (source: ci.yml:71)
  • Tests: build\build-tests.ps1 (default Debug) first restores the parent hmailserver\test\hMailServer Tests.sln (/t:Restore /p:RestorePackagesConfig=true) then MSBuilds hmailserver\test\RegressionTests\RegressionTests.sln x64, log to logs\build-tests.log (source: build/build-tests.ps1:18-64, code)
  • Installer: ISCC on hmailserver\installation\hMailServer64.iss (Inno Setup 6) (source: README.md:292-293; RELEASE.md:88). ISCC on this machine: C:\Users\chris\AppData\Local\Programs\Inno Setup 6\ISCC.exe (local, code). hMailServer64.iss reads hMailServerLibs and defines DOTNET_MAJOR "10", DOTNET_CHANNEL "10.0", DOTNET_RUNTIME_FILE "windowsdesktop-runtime-10.0-win-x64.exe" (source: hmailserver/installation/hMailServer64.iss:1-19, code)
  • Bundled .NET runtime: build\get-dotnet-runtime.ps1 (default -Channel 10.0) downloads https://aka.ms/dotnet/10.0/windowsdesktop-runtime-win-x64.exe to hmailserver\installation\DotNet\ (gitignored, 55+ MB) and rejects files under 40 MB (source: build/get-dotnet-runtime.ps1:16-38, code)
  • COM wrapper regeneration after any hMailServer.idl change and after a Release server build: build\regenerate-interop.ps1 runs TlbImp.exe hMailServer.tlb /out:Interop.hMailServer.dll /namespace:hMailServer /machine:X64 /silent AND rewrites the wrapper's SHA-256/size in hmailserver/docs/third-party-binaries.json (source: build/regenerate-interop.ps1:30-130, code). TlbImp default path C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\x64\TlbImp.exe (source: build/regenerate-interop.ps1:31)
  • COM re-registration for a NEW interface (elevated): build\register-com.ps1 (/RegisterTypeLib, or /Register with -IncludeService) (source: build/register-com.ps1:39-93, code)
  • Fuzzing (separate, clang-cl): .\fuzz\build-fuzz.ps1 then .\fuzz\run-fuzz.ps1 -Target mime_message_fuzzer -Minutes 5; needs "C++ Clang tools for Windows"; never touches hMailServer.sln or the service (source: fuzz/README.md:16-26, doc). Harnesses: mime_message_fuzzer, mime_header_fuzzer, mime_decode_fuzzer; dict fuzz/dict/mime.dict; fuzz/regression/ committed; corpus/, build/, bin/, artifacts/ are generated not committed (source: fuzz/README.md:32-45; ls fuzz/harness fuzz/dict fuzz/regression, code)

The build graph

Nothing here builds everything. Five separate commands produce five separate outputs, and two of them have an ordering constraint that is easy to miss — the interop wrapper is generated from the server build and consumed by the tools build, so a release that regenerates it must do so between them.

flowchart TB
    subgraph outside["Outside the tree - %hMailServerLibs%"]
        OSSL["OpenSSL 4.0.2<br/>libraries/build-openssl.ps1"]
        BOOST["Boost 1.92.0<br/>libraries/build-boost.ps1"]
        PGL["PostgreSQL 18.3 libpq<br/>libraries/build-pgsql.ps1"]
    end

    SLN["hMailServer.sln<br/>C++, v145, x64"]
    EXE["hMailServer.exe<br/>+ hMailServer.tlb"]
    POSTB["build/post-build.ps1 - elevated<br/>stops the service, copies OpenSSL,<br/>libpq and MariaDB Connector/C DLLs,<br/>hMailServer.exe /Register"]
    TLBIMP["build/regenerate-interop.ps1<br/>TlbImp + rewrites the SHA-256<br/>in docs/third-party-binaries.json"]
    INTEROP["Interop.hMailServer.dll<br/>committed under source/Tools/Interop"]
    TOOLS["hMailServer Tools.sln<br/>DBSetup, DBSetupQuick, DBUpdater,<br/>DataDirectorySynchronizer, ImportTool"]
    CP["ControlPanel.sln<br/>hMailCP - published separately"]
    TESTS["RegressionTests.sln<br/>net481, x64 only"]
    DOTNET["build/get-dotnet-runtime.ps1<br/>windowsdesktop-runtime-10.0-win-x64.exe"]
    ISS["ISCC hMailServer64.iss"]
    SETUP["hMailServer-6.2.28-x64.exe"]

    OSSL --> SLN
    BOOST --> SLN
    PGL --> SLN
    SLN --> EXE
    EXE --> POSTB
    EXE -->|"only when hMailServer.idl changed"| TLBIMP
    TLBIMP --> INTEROP
    INTEROP --> TOOLS
    INTEROP --> CP
    INTEROP --> TESTS
    EXE --> ISS
    TOOLS --> ISS
    CP --> ISS
    DOTNET --> ISS
    ISS --> SETUP
Loading
Output Command Configuration default Lands in
hMailServer.exe build\build.ps1 Debug (pass -Configuration Release) hmailserver\source\Server\hMailServer\x64\<cfg>\
Registered service + runtime DLLs build\post-build.ps1 Release when told beside the exe; elevates via UAC
Interop.hMailServer.dll build\regenerate-interop.ps1 hmailserver\source\Tools\Interop\ (committed)
The five admin tools build\build-tools.ps1 Release each project's own publish\
hMailCP (Control Panel) dotnet publish ControlPanel.csproj -c Release -o publish ControlPanel\publish\not covered by build-tools.ps1
RegressionTests.dll build\build-tests.ps1 Debug hmailserver\test\RegressionTests\bin\x64\Debug\
The installer ISCC on hMailServer64.iss installation\Output\

Three traps this graph encodes:

  • A stale interop wrapper still compiles. The tools use a small, stable subset of the COM API, so nothing fails — the members added this release are simply invisible to them. That is why regenerating is a release step rather than something a build error reminds you about.
  • Regenerate the wrapper after any interface-ordering fix, never before, or the old vtable layout is baked into the shipped wrapper permanently.
  • The tests are always built x64. An AnyCPU build of the same project also runs, against stale assemblies, and passes tests it never executed.

4. Build hardening and reproducibility flags (server)

  • Warnings as errors: <TreatWarningAsError>true</TreatWarningAsError> (/WX), <WarningLevel>Level3</WarningLevel>, Debug and Release (source: hMailServer.vcxproj:83-84,153-154, code)
  • Control Flow Guard: <ControlFlowGuard>Guard</ControlFlowGuard> compile + /guard:cf link, Debug and Release (source: hMailServer.vcxproj:87,96,164,173, code)
  • Reproducible Release: compiler /Zm200 /Brepro /d1trimfile:$(SolutionDir), linker /ignore:4099 /guard:cf /Brepro /pdbaltpath:%_PDB% (source: hMailServer.vcxproj:165,173, code)
  • OPENSSL_NO_FILENAMES defined in every configuration (source: hMailServer.vcxproj:64,77,137,149,168, code)
  • <ExceptionHandling>Async</ExceptionHandling> (/EHa) in Debug and Release (source: hMailServer.vcxproj:79,150, code)
  • /GS, /NXCOMPAT, /DYNAMICBASE are NOT set explicitly in the vcxproj (no BufferSecurityCheck/DataExecutionPrevention/RandomizedBaseAddress elements) - they are MSVC x64 defaults. Verified on the built binary instead: x64\Release\hMailServer.exe PE DllCharacteristics 0xC160 = HIGH_ENTROPY_VA, DYNAMIC_BASE, NX_COMPAT, GUARD_CF, TERMINAL_SERVER_AWARE on; LOAD_CONFIG directory present (320 bytes) (source: PE header parse of the local Release exe, code)
  • The PE-flag check above was made on the 6.2.24 binary (SHA-256 64406163b1a3a8a865e34d74883b5e2a05a5a1c476b500b26745cde67d8a989b, size 10,171,904, identical to the hash in the v6.2.24 release notes); the project flags have not changed since. The shipped 6.2.28 binary is hMailServer.exe 6.2.28.38, SHA-256 9E89C1E3ABF4DD36560B0B5F3515BDA3DAB8B96F00EF35DA5376A9B60176BD15; the installer hMailServer-6.2.28-x64.exe is 79,324,526 bytes, SHA-256 FCC791855E9C1C4F3B2D3B8592B4D9CCBAC000DA866BBF8617561415078D5054 (source: v6.2.28 release notes, verification section, and gh release view v6.2.28 --json assets, gh)
  • Reproducibility claim: two clean Release builds give a byte-identical exe; first shown 22 August 2026; verified per release at RELEASE.md step 8 (source: ASSURANCE-CASE.md "Residual risk"; RELEASE.md:69-78, doc)
  • CFG cost measured: full 1,838-test gate 31 minutes vs about 55 before; exe grew about 97 KB (source: ASSURANCE-CASE.md "Residual risk", doc)

5. Running the tests

  • Runner: build\run-tests.ps1 runs hmailserver\test\packages\NUnit.ConsoleRunner.3.22.0\tools\nunit3-console.exe hmailserver\test\RegressionTests\bin\x64\Debug\RegressionTests.dll --labels=All, log to logs\run-tests.log; -StopOnError adds /stoponerror (not for release runs); -Where "<expr>" adds --where= for a subset (source: build/run-tests.ps1:14-69, code)
  • Test assembly is the Debug x64 build even for release gates (source: build/run-tests.ps1:28, code)
  • Pre-flight first: build\preflight-tests.ps1 (-Clean removes a stale ERROR log) (source: RELEASE.md:23-27; .github/PULL_REQUEST_TEMPLATE.md:16-18)
  • Pre-flight checks (in order): service exists and its PathName is the repo Release exe; service running; no stray HKLM\SOFTWARE\hMailServer\InstallLocation in either registry view; COM auth as Administrator/testar; Domains.Count -eq 1; Settings.TCPIPPorts.Count -eq 4; Database.CurrentVersion -eq RequiredVersion; no stale ERROR log; clamd listening on 3310; service SpamAssassinJAM exists; listeners on 25, 110, 143; VPN adapter warning (Proton|WireGuard); NUnit runner and RegressionTests.dll present; no orphan .cs files missing from RegressionTests.csproj (source: build/preflight-tests.ps1:19-159, code)
  • Test count: 2,127 [Test] attributes across the suite at v6.2.28 (2,024 at v6.2.27); RegressionTests.csproj lists 348 <Compile Include> entries explicitly (no glob) (source: grep -c "^\s*\[Test\]" over hmailserver/test/RegressionTests; csproj, code). The v6.2.28 gate was 2,127 tests, 2,119 passed, 0 failed, 8 skipped - the 7 explicit stress skips and one ignored where STARTTLS is not offered on the plain IMAP port of this bench - on the stamped binary. The notes also record 65 schema probes passing against a 6031 database built from the create script; unlike v6.2.27's they say nothing about the Control Panel's own tests (source: v6.2.28 release notes, gh)
  • Every source file must be listed in RegressionTests.csproj or it is never compiled; pre-flight fails on orphans (source: ARCHITECTURE.md "Tests"; preflight-tests.ps1:133-159)
  • Tests that provoke a reported error must clear it with CustomAsserts.AssertReportedError(...); PerformBasicSetup calls AssertNoReportedError (source: ARCHITECTURE.md "Tests", doc)
  • Run elevated: the suite starts/stops the hMailServer and SpamAssassin services; unelevated those tests report inconclusive (source: README.md:519-520, doc)
  • Never dotnet test the RegressionTests project; it is .NET Framework/packages.config and is built with MSBuild (source: build-tests.ps1; RegressionTests.csproj:38, code)
  • Control Panel unit tests: dotnet test hmailserver/source/Tools/ControlPanel.Tests/ControlPanel.Tests.csproj --configuration Release --collect:"XPlat Code Coverage" (cobertura) in CI; the coverage upload to GitHub is fail-on-error: false because the Code Quality repository setting is deliberately off (source: ci.yml:77-148, code)
  • Import Tool unit tests: dotnet test hmailserver/source/Tools/ImportTool.Tests/ImportTool.Tests.csproj -c Release --no-build runs in the "Build .NET tools" job after the solution build (the Maildir reader: folders, messages, file-name flags, line endings) (source: ci.yml:118-126, code)
  • Other test projects under hmailserver/test (PerformanceTests, MemoryTests, StressTest, TestBedUI, TestInvalidConnections, VMTestRunner.Console, PerformanceTest, hMailServer.PerformanceTests, hMailServer.PerformanceTests.Console, hMailServer.Test.Infrastructure) exist but are built by nothing (source: find hmailserver/test -name *.csproj; no reference in build/ or .github/workflows, code)

The pre-flight, check by check

build/preflight-tests.ps1 runs these in order and stops at the first that fails. Every one of them corresponds to a run that was actually lost. The right-hand column is what the failure looks like if you skip the pre-flight and run the suite anyway — which is the reason the script exists, because none of those symptoms names its cause.

# Check What it is really catching Symptom without the pre-flight
1 Service exists, PathName is the repo Release exe Somebody ran the installer on this machine and it re-pointed the service at C:\Program Files\... Every run is green — against the installed binary, not the one you just built
2 Service running A failed link left the service stopped 100% of tests fail in fixture setup
3 No stray HKLM\SOFTWARE\hMailServer\InstallLocation in either registry view The same installer, second half of its damage: the INI lookup is redirected The server reports "Running" with empty configuration
4 COM auth as Administrator / testar The bench credential was changed or cleared Setup throws on the first COM call
5 Domains.Count -eq 1 A killed fixture left a domain behind, or removed the test domain Fixtures that assume a clean domain list fail in unrelated places
6 Settings.TCPIPPorts.Count -eq 4 A TLS fixture was interrupted: it registers twelve extra ports and removes them in teardown Port-count sixteen; TLS fixtures fail on "already exists"
7 Database.CurrentVersion -eq RequiredVersion The bench database was never moved forward past a schema bump The service refuses the connection and nothing in the failure says "database"
8 No stale ERROR log An aborted run left the deliberate scanner error behind 100% of tests fail before doing anything
9 clamd listening on 3310 ClamAV not started, or still loading signatures Anti-virus fixtures fail open — the server has no verdict, so nothing is detected
10 Service SpamAssassinJAM exists Wrapped under a different name The fixture that stops and restarts it cannot find it
11 Listeners on 25, 110, 143 Another mail server, or a port left bound Connection-refused failures that look like protocol bugs
12 VPN adapter warning (Proton, WireGuard) Loopback rewriting Fixtures that bind a specific local address pick the wrong one
13 NUnit runner and RegressionTests.dll present The tests were never built, or built AnyCPU The runner exits having run nothing
14 No orphan .cs files missing from RegressionTests.csproj A new test file nobody added to the explicit file list The test is invisible: it is not run and a green suite says nothing about it

Pre-flight passed - safe to run the suite is the state Regression Test Environment builds a machine towards.

6. Regression environment (what the suite needs)

  • Real server, nothing mocked: live SMTP/IMAP/POP3 sockets, COM API, live SpamAssassin and ClamAV, real TLS 1.2/1.3 handshakes. DNS is the one exception since 5 September 2026: the suite serves its own zone (Shared/SuiteDns.cs, a [SetUpFixture] that binds a FakeDnsServer on 127.0.0.1 and points DNSServer at it for the run; DMARC, SPF and DKIM fixtures add the records they need) so no test resolves a live name (source: SuiteDns.cs; RegressionEnvironment.md:189-191, code). README.md:13 and ARCHITECTURE.md:170 still say "DMARC against live DNS" - stale.
  • The service must run from the repository Release build hmailserver\source\Server\hMailServer\x64\Release\hMailServer.exe, not an installed copy (source: hmailserver/docs/RegressionEnvironment.md:35-41; preflight-tests.ps1:20-24)
  • Database: SQL Server Compact 4.0 x64 from hmailserver\installation\SQLCE; bench DB created once via COM Database.CreateInternalDatabase() + Reinitialize(), thereafter upgraded never recreated; move it forward with build\upgrade-test-database.ps1 (through the service's COM API, no elevation) (source: RegressionEnvironment.md:63-87; build/upgrade-test-database.ps1:4-22, code)
  • Data directory convention C:\HMTest\Data (short, to avoid MAX_PATH) (source: RegressionEnvironment.md:89-95)
  • Exactly one domain and exactly four TCP/IP ports at start: SMTP 25, POP3 110, IMAP 143 and the SMTP submission port; the TLS fixtures add twelve more and remove them in teardown (source: RegressionEnvironment.md:96-105; preflight-tests.ps1:53-59)
  • COM credentials: Administrator / testar (blank fallback) - a bench credential, not a secret (source: RegressionEnvironment.md:107-110; preflight-tests.ps1:50)
  • ClamAV: C:\clamav\clamd.exe (hard-coded in CustomAsserts), TCPSocket 3310, TCPAddr 127.0.0.1, runs as a bare process (source: RegressionEnvironment.md:112-120)
  • SpamAssassin: Windows service named SpamAssassinJAM wrapping spamd.exe -i 127.0.0.1 -A 127.0.0.1 -p 783 (source: RegressionEnvironment.md:127-134). README.md:527-535 gives an alternative CPAN build with --port 783.
  • Without SpamAssassin/ClamAV the dependent tests report inconclusive rather than failing (source: README.md:512, doc)
  • The suite wipes domains and accounts; never run it against production (source: README.md:514; RegressionEnvironment.md:17-18)

7. Contribution rules

  • Code of Conduct: Contributor Covenant (source: .github/CODE_OF_CONDUCT.md:1; CONTRIBUTING.md:5-6)
  • DCO sign-off: git commit -s adds Signed-off-by:; no CLA; fix is git commit --amend -s (source: .github/CONTRIBUTING.md:40-48)
  • The DCO check (.github/workflows/dco.yml, job "Signed-off-by on every commit") runs on pull_request to master only, requires the trailer to contain the commit AUTHOR's email, and is skipped entirely when the PR author is dependabot[bot] or has author_association OWNER or MEMBER (source: dco.yml:28-42,59-70, code)
  • The DCO job is NOT among the nine required status checks on master (source: gh api repos/Progressiverobot/hmailserver/rulesets/21165267, gh) - see section 8
  • Branch from master; version branches are bug-fix only; one logical change per PR; add/update regression tests; parameterised SQL exclusively; new server-wide optional features follow the INI-settings pattern (IniFileSettings getter + Server features dialog control) (source: CONTRIBUTING.md:50-57)
  • Coding style: .editorconfig sets indent_style = space, indent_size = 3 for *.{h,cpp,cs} (source: .editorconfig:4-6, code). Enforced by editorconfig-checker v3.11.3 (installed by the action, then run explicitly - the action alone checks nothing) and dotnet format --verify-no-changes on the Tools solution and ControlPanel.csproj; exclusions live in .editorconfig-checker.json at the repository root (source: .github/workflows/style.yml:9,40-52,102-127; .editorconfig-checker.json, code).
  • Every source file carries a copyright line and SPDX-License-Identifier: AGPL-3.0-or-later; enforced by python3 build/add-license-headers.py --check; MIDL output (dlldata.c, hMailServer_i.c, hMailServer_p.c) and the directories libraries/, zlib (vendored zlib source under its own licence; new in 6.2.28), obj, bin, publish, packages, Output are skipped (source: build/add-license-headers.py:53-59; style.yml:55-66, code)
  • The "SPDX identifier on every source file" job also runs, in order: python3 build/check-mnemonics.py (every static Control Panel caption has an Alt-key mnemonic, no two on one view/method share one), python3 build/check-localisation.py (the generated English catalogue Resources/Strings.resx matches the L(...)/F(...)/N(...)/{loc:L} marks in the source - run it with --write after adding a caption; every language keeps its English's {n} placeholders and Alt keys; each of the 17 languages in COMPLETE must translate every key; a file in LOCALISED may carry no unmarked caption), python3 build/check-catalogues.py (translations keep the server's literal names, page titles and numbers), python3 build/check-ini-coverage.py (every [Settings] key IniFileSettings reads is named somewhere in the Control Panel and is read through IniFileSettings), python3 build/check-authz-choke-point.py (only ACLManager decides folder access). All five gate a PR through that required check; the mnemonic and ACLManager checks landed for 6.2.25, the three localisation and INI-coverage checks for 6.2.28 (source: .github/workflows/style.yml:65-100; build/check-localisation.py:52-55, code)
  • Compiler runs with /WX; code must build warning-free; C# builds with -warnaserror in CI (source: CONTRIBUTING.md:19; ci.yml:68,116)
  • PR template checklist: builds warning-free with build/build.ps1; regression suite passes after preflight; new behaviour covered by tests; parameterised SQL; schema changes include MySQL, MS SQL and PostgreSQL scripts in source/DBScripts/; new settings via COM API or hMailServer.INI pattern (source: .github/PULL_REQUEST_TEMPLATE.md:14-22)
  • good first issue label marks bounded tasks tied to Roadmap.md gaps (source: CONTRIBUTING.md:33-38)
  • CODEOWNERS: @chrisholloway5 owns everything, with explicit entries for .github, workflows, SECURITY.md, ThirdPartyBinaries docs, libraries/, installation/, Interop/, TCPIP/, Crypt.cpp/h, LDAP/, RELEASE.md, build/; "Require review from Code Owners" is deliberately OFF because a sole owner cannot approve their own PR (source: .github/CODEOWNERS:1-58, code; ruleset require_code_owner_review: false, gh)
  • Support routing: defects to Issues; questions to Discussions; general hMailServer questions to the hMailServer forum; security to advisories (source: .github/SUPPORT.md; .github/ISSUE_TEMPLATE/config.yml)
  • Dependabot: NuGet weekly grouped (dotnet-dependencies, limit 10) and GitHub Actions weekly grouped (limit 5) for both / and /.github/actions/native-dependencies; native C++ libs are pinned by hand in hMailServer.vcxproj and the digest tables of libraries/build-*.ps1 (source: .github/dependabot.yml, code)
  • Commit identity for the maintainer: commit as chrisholloway5, no co-author trailers (source: RELEASE.md:91-92)
  • Contributions are licensed AGPLv3 (source: CONTRIBUTING.md:66-69)

8. CI workflows and required checks

Twelve workflow files under .github/workflows/ (names as declared, code):

  • ci.yml "CI" - push/PR to master, dispatch. Jobs: "Build Control Panel (.NET 10)" (regenerates and verifies SettingsSearchIndex.g.cs, restore --locked-mode, build -warnaserror, publish, test with coverage), "Build .NET tools" (locked restore, build -warnaserror, ./build/build-tools.ps1), "upload-coverage-csharp" (source: ci.yml:12-148)
  • codeql.yml "CodeQL" - push/PR/weekly Monday 04:32 UTC/dispatch. Job "Analyze (csharp)" on every push/PR, security-and-quality, build-mode manual, obj/bin results filtered from SARIF. Job "Analyze (c-cpp)" on push to master, schedule and dispatch but NOT on pull requests (if: github.event_name != 'pull_request'); GitHub-hosted windows-2025-vs2026, native libraries restored or built by .github/actions/native-dependencies before CodeQL init, security-extended, full build via build\build.ps1 -Configuration Release (Debug selectable on dispatch), 240-minute timeout; the status-check name carries the image, "Analyze (c-cpp) (windows-2025-vs2026)" (source: codeql.yml:34-47,160-259; .github/codeql/codeql-config.yml)
  • dco.yml "DCO" - PR only; job "Signed-off-by on every commit" (source: dco.yml:1-37)
  • dependency-review.yml "Dependency review" - PR only; fail-on-severity: high (source: dependency-review.yml:1-30)
  • installer-smoke.yml "Installer smoke test" - dispatch only; job "Install on a throwaway runner and verify": downloads hMailServer-*-x64.exe from a release tag or an installer artifact, runs /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /TYPE=full /LOG=, 10-minute bound, checks service PathName, Running, listeners on 25/110/143, New-Object -ComObject hMailServer.Application, keeps the log, uninstalls (source: installer-smoke.yml:18-223)
  • sbom.yml "SBOM" - push to master, release: created, dispatch with release_tag; job "Generate SBOM (SPDX + CycloneDX)" with anchore/sbom-action (Syft), then build/merge-native-dependencies-into-sbom.ps1 adds OpenSSL/Boost/libpq (versions parsed from hMailServer.vcxproj by build/native-dependencies.ps1), verifies every native PURL is present, uploads artifact (90 days) and attaches to the release (source: sbom.yml:24-133)
  • scorecard.yml "OpenSSF Scorecard" - Sunday 04:17 UTC, push to master, dispatch; publish_results: true since 5 September 2026 (the README badge and the scorecard.dev viewer read the public OpenSSF API); SARIF to code scanning (source: scorecard.yml:18-95, code)
  • server-build.yml "Server build" - push/PR to master, dispatch (Release/Debug); job "Build server" on GitHub-hosted windows-2025-vs2026 (so the status-check name is "Build server (windows-2025-vs2026)"); the native libraries are restored from cache or built from source by .github/actions/native-dependencies (libraries/build-*.ps1, 30-45 minutes cold), then build\build.ps1 -Configuration Release; uploads hMailServer.exe, .pdb, hMailServer.tlb and the OpenSSL/libpq DLLs as a 14-day artifact; 120-minute timeout; the regression suite is NOT run here - it still needs the maintainer's bench (source: server-build.yml:1-150, code)
  • sign-release.yml "Sign release artefacts" - release: created, dispatch with tag; job "Sign and attach signatures": refuses a lightweight tag or one that fails verify-tag against .github/allowed_signers; cosign sign-blob --yes --bundle <file>.cosign.bundle; verifies with --certificate-identity-regexp "^https://github\.com/<repo>/" and --certificate-oidc-issuer https://token.actions.githubusercontent.com; uploads bundles; before signing it refuses a release with no asset named exactly hMailServer-<tag without the leading v>-x64.exe, because the server's update checker (UpdateChecker.cpp:428-429, new in 6.2.28) builds that name from the tag and matches it on equality (source: sign-release.yml:38-189, name check at 139-160)
  • style.yml "Coding style" - jobs "editorconfig-checker", "SPDX identifier on every source file", "dotnet format leaves the C# tree unchanged" (source: style.yml:22-82)
  • upstream-watch.yml "Upstream watch" - monthly (0 6 1 * *) and dispatch; compares against hmailserver/hmailserver after the SHA in .github/upstream-sync; issues: write (source: upstream-watch.yml:14-35)
  • verify-binary-provenance.yml "Binary provenance" - push/PR/dispatch; job "Verify committed binaries against the manifest": hash every artifacts[] entry in hmailserver/docs/third-party-binaries.json, fail on changed/missing/unlisted binaries (Scorecard extension set + MZ/ELF/OLE sniff), warn on remove-* dispositions (source: verify-binary-provenance.yml:35-166)
  • All actions are pinned to full commit SHAs with version comments (source: every uses: line, code)

Branch and tag protection (gh, ruleset API, 2026-09-04):

  • Ruleset "Protect master: changes arrive by pull request" (id 21165267, active) on refs/heads/master: rules deletion, non_fast_forward, pull_request (required_approving_review_count 0, require_code_owner_review false, dismiss_stale_reviews false, require_last_push_approval false, allowed merge methods merge/squash/rebase, require_extra_approval_for_unattributed_changes true), required_status_checks with strict_required_status_checks_policy: true
  • The nine required status checks, verbatim: "Build Control Panel (.NET 10)", "Build .NET tools", "Analyze (csharp)", "SPDX identifier on every source file", "editorconfig-checker", "dotnet format leaves the C# tree unchanged", "Verify committed binaries against the manifest", "Dependency review", "Build server (windows-2025-vs2026)"
  • Not required: "Signed-off-by on every commit" (DCO), Scorecard, CodeQL c-cpp, SBOM, upstream watch
  • Ruleset "Protect release tags" (id 20539388, active) on refs/tags/v*: deletion and non_fast_forward blocked
  • Classic branch protection is not used (/branches/master/protection returns 404 "Branch not protected"); protection is entirely via rulesets

What a pull request has to pass

Nine status checks are required on master. They come from six workflows; the other six workflows either do not run on pull requests at all, or run and are advisory. The diagram is the PR path only — the schedule and release paths are in the table below.

flowchart LR
    PR["Pull request<br/>to master"]

    subgraph runs["Workflows that run on a PR"]
        direction TB
        CI["CI - ci.yml<br/>windows-latest"]
        SBW["Server build - server-build.yml<br/>windows-2025-vs2026, 120 min"]
        CQ["CodeQL - codeql.yml<br/>csharp only on a PR"]
        STY["Coding style - style.yml"]
        BP["Binary provenance<br/>verify-binary-provenance.yml"]
        DRV["Dependency review<br/>dependency-review.yml"]
        DCO["DCO - dco.yml"]
    end

    subgraph req["The nine required checks"]
        direction TB
        R1["Build Control Panel (.NET 10)"]
        R2["Build .NET tools"]
        R3["Build server (windows-2025-vs2026)"]
        R4["Analyze (csharp)"]
        R5["editorconfig-checker"]
        R6["SPDX identifier on every source file"]
        R7["dotnet format leaves the C# tree unchanged"]
        R8["Verify committed binaries against the manifest"]
        R9["Dependency review"]
    end

    ADV["Signed-off-by on every commit<br/>runs, but NOT required -<br/>and skipped for OWNER, MEMBER<br/>and dependabot"]
    MERGE["Merge - strict policy:<br/>the branch must be up to date<br/>with master first"]

    PR --> CI
    PR --> SBW
    PR --> CQ
    PR --> STY
    PR --> BP
    PR --> DRV
    PR --> DCO

    CI --> R1
    CI --> R2
    SBW --> R3
    CQ --> R4
    STY --> R5
    STY --> R6
    STY --> R7
    BP --> R8
    DRV --> R9
    DCO --> ADV

    R1 --> MERGE
    R2 --> MERGE
    R3 --> MERGE
    R4 --> MERGE
    R5 --> MERGE
    R6 --> MERGE
    R7 --> MERGE
    R8 --> MERGE
    R9 --> MERGE
Loading

The "SPDX identifier on every source file" job is doing far more than its name says. It runs six scripts in order, and any of them fails the required check:

flowchart TB
    J["Job: SPDX identifier on every source file<br/>style.yml, ubuntu-latest"]
    J --> A1["build/add-license-headers.py --check<br/>copyright line + SPDX on every source file"]
    A1 --> A2["build/check-mnemonics.py<br/>every static Control Panel caption has an Alt key;<br/>no two on one view share one"]
    A2 --> A3["build/check-localisation.py<br/>the English catalogue matches the source marks;<br/>all 17 COMPLETE languages translate every key"]
    A3 --> A4["build/check-catalogues.py<br/>translations keep literal names, page titles, numbers"]
    A4 --> A5["build/check-ini-coverage.py<br/>every Settings key the server reads<br/>has a Control Panel editor"]
    A5 --> A6["build/check-authz-choke-point.py<br/>only ACLManager decides folder access"]
Loading

That is why adding one Control Panel caption is not a one-line change: a new caption fails CI in every language until it is marked, the English catalogue is regenerated with python3 build/check-localisation.py --write, and all 17 complete languages carry a translation.

Every workflow, and what each would catch

Workflow File Runs on Runner Required What a failure means
CI ci.yml push/PR to master, dispatch windows-latest yes (2 checks) The Control Panel or the .NET tools no longer build warning-free, a locked NuGet restore drifted, SettingsSearchIndex.g.cs is stale, or a Control Panel / Import Tool unit test failed
Server build server-build.yml push/PR to master, dispatch windows-2025-vs2026, 120 min yes The C++ server does not compile on a clean hosted machine with libraries built from source. The regression suite is not run here — that still needs the maintainer's bench
CodeQL codeql.yml push/PR/weekly Mon 04:32 UTC/dispatch windows-latest (csharp), windows-2025-vs2026 (c-cpp), 240 min csharp only A security-and-quality finding in C#. The C++ job uses security-extended and deliberately does not run on pull requests
Coding style style.yml push/PR to master, dispatch ubuntu-latest ×2, windows-latest ×1 yes (3 checks) Indentation drift, a missing SPDX header, an unformatted C# file, or any of the five Control Panel / INI / authorisation checks above
Binary provenance verify-binary-provenance.yml push/PR to master, dispatch ubuntu-latest yes A committed binary changed, went missing, or appeared without a docs/third-party-binaries.json entry. Regenerating the interop wrapper by hand without updating the manifest broke this twice in one day
Dependency review dependency-review.yml PR only ubuntu-latest yes A dependency change introduces a high-or-worse advisory
DCO dco.yml PR only ubuntu-latest no A commit lacks Signed-off-by: matching its author. Skipped entirely for dependabot[bot] and for authors with OWNER or MEMBER association
SBOM sbom.yml push to master, release: created, dispatch with release_tag ubuntu-latest no Syft could not build the SPDX/CycloneDX documents, or a native PURL (OpenSSL, Boost, libpq) is missing after the merge step
Sign release artefacts sign-release.yml release: created, dispatch with tag ubuntu-latest no A lightweight or unverifiable tag, or a release with no asset named exactly hMailServer-<tag without the v>-x64.exe
OpenSSF Scorecard scorecard.yml Sunday 04:17 UTC, push to master, dispatch ubuntu-latest no Score regression. Published to the OpenSSF API since 5 September 2026; Branch-Protection and Code-Review are expected to fail on a single-maintainer repository
Upstream watch upstream-watch.yml monthly 0 6 1 * *, dispatch ubuntu-latest no Nothing — it opens an issue listing upstream commits after the SHA in .github/upstream-sync
Installer smoke test installer-smoke.yml dispatch only windows-latest no The installer does not install silently, or the installed service does not run, listen on 25/110/143 and answer a hMailServer.Application COM call

Two structural facts about that table:

  • Nothing in CI runs the regression suite. The 2,127-test gate is a bench-only step (RELEASE.md 8b and 9), which is why "green CI" and "green gate" are different claims and the release notes report them separately.
  • The two release workflows are dispatched by hand. GitHub delivers no release event for a draft release — measured here, not assumed — so the release: created trigger only covers a release published without a draft, which is the path that can no longer attach anything. See Release Process.

9. Release process (outline)

The thirteen ordered steps of RELEASE.md (source: RELEASE.md:6-153, doc, with script facts checked in code):

  1. Freeze the code (RELEASE.md:8)
  2. Adversarial review of the complete diff BEFORE version stamping (RELEASE.md:10-17)
  3. Fix what survives; every defect fix gets a negative-control test that fails on the pre-fix binary (RELEASE.md:18-22)
  4. Pre-flight: build\preflight-tests.ps1 (-Clean) (RELEASE.md:23-27)
  5. build\check-roadmap.ps1 reconciles Roadmap.md tick boxes against section counts and the contents table (RELEASE.md:28-31; build/check-roadmap.ps1:4-20)
  6. build\check-db-scripts.ps1 builds a throwaway SQL CE database from CreateTablesMSSQL.sql and applies every Upgrade*MSSQLCE.sql using SQLScriptParser's blank-line splitting (default assembly C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop\System.Data.SqlServerCe.dll); plus build\check-schema-versions.ps1 (pwsh 7 only) which cross-checks REQUIRED_DB_VERSION, DBUpdater's LoadScripts table, GetDatabaseVersionName, the Upgradeto.sql files (four per step) and SchemaVerification.cs probes (RELEASE.md:32-46; build/check-db-scripts.ps1:26-135; build/check-schema-versions.ps1:4-18)
  7. Version stamp: Version.h (three defines), section_setup_64.iss, seven .csproj <Version>; git grep <old-version> (RELEASE.md:48-50)
  8. Build everything at the stamped version: build.ps1 -Configuration Release, build-tools.ps1 -Configuration Release, ControlPanel dotnet publish, build-tests.ps1; confirm FileVersion on hMailServer.exe and publish\hMailCP.dll; regenerate the interop wrapper if the IDL changed (AFTER any interface-ordering fix); prove reproducibility by a second build\build.ps1 -Configuration Release -Clean and compare SHA-256; put the hash in the notes (RELEASE.md:51-78) 8b. Full regression suite on the assertion build first (build.ps1 -Configuration Release -Asserts), expecting no HM6364 line - a violation fails the test that provoked it; then rebuild plain Release before step 9 (RELEASE.md step 8b, added 5 Sep 2026)
  9. Full regression suite on the stamped binary; any change afterwards voids the run; never abort (RELEASE.md:79-83)
  10. README/release notes: "Fixed" means reproduced-then-fixed or negative-control-tested; otherwise "hardened"/"instrumented"/"still open" (RELEASE.md:84-87,164-167)
  11. Installer: ISCC on hMailServer64.iss; never run it on the dev machine; validation is the installer-smoke.yml workflow (RELEASE.md:88-90)
  12. Commit as chrisholloway5 (no co-author trailers); master is protected so: git push origin <branch>, gh pr create --base master --head <branch> --fill, gh pr merge --rebase (self-merge allowed, no reviewer required), git pull --ff-only origin master. Then an annotated SIGNED tag: git tag -s vX.Y.Z -m "hMailServer X.Y.Z", git -c gpg.ssh.allowedSignersFile=.github/allowed_signers verify-tag vX.Y.Z, git push origin vX.Y.Z. Signing is SSH (gpg.format ssh, user.signingkey = public key path); the key must be in .github/allowed_signers. Then publish as a DRAFT: gh release create vX.Y.Z <installer> --draft --prerelease --title "..." --notes-file <notes>, gh workflow run "SBOM" -f release_tag=vX.Y.Z, gh workflow run "Sign release artefacts" -f tag=vX.Y.Z (LAST), gh release view vX.Y.Z --json assets (expect installer + 2 SBOMs + bundles), gh release edit vX.Y.Z --draft=false (RELEASE.md:91-151). The installer asset must be named exactly hMailServer-<tag without the leading v>-x64.exe (with its .cosign.bundle beside it): the update checker in every running server builds that name from the tag and matches it exactly, and the signing workflow refuses a release that lacks it (RELEASE.md:159-167; sign-release.yml:139-160). v6.2.22-pre3 shipped hMailServer-6.2.22-x64.exe under that tag, which is the case the check exists for.
  13. Close the loop on every issue the release resolves or does not (RELEASE.md:152-153)

Mechanics that are facts rather than advice:

  • Immutable releases are enabled: a published release refuses further asset uploads (HTTP 422); a tag that backed an immutable release can never back another, even after deletion - a broken release needs the next version number (source: RELEASE.md:124-151; sbom.yml:7-12; sign-release.yml:6-9)
  • Draft releases deliver no release event, so both the SBOM and signing workflows MUST be dispatched by hand with the tag (source: RELEASE.md:141-147; sbom.yml:14-17; sign-release.yml:40-44)
  • Local git config on the maintainer's machine: gpg.format ssh, user.signingkey C:/Users/chris/.ssh/id_ed25519.pub (source: git config --local, code)
  • Allowed signer: principal christopher.j.holloway@outlook.com, key type ssh-ed25519 (source: .github/allowed_signers:11, code)
  • Standing rules: prefer two small releases over one large; a fix to a shared layer needs a dependents audit; the public record never overstates (source: RELEASE.md:155-167)
  • The installer smoke test ran twice on 2026-09-04 (17:46 and 18:42 UTC), both success, the second at the v6.2.24 publish time (source: gh run list --workflow=installer-smoke.yml, gh)

10. Verifying a release (what a user can check)

  • Tag signature: git -c gpg.ssh.allowedSignersFile=.github/allowed_signers verify-tag v6.2.24 (source: SECURITY.md:86-92; .github/allowed_signers:5)
  • Asset signature: cosign verify-blob --bundle hMailServer-x.y.z-x64.exe.cosign.bundle --certificate-identity-regexp '^https://github\.com/Progressiverobot/hmailserver/' --certificate-oidc-issuer https://token.actions.githubusercontent.com hMailServer-x.y.z-x64.exe (source: SECURITY.md:94-104; sign-release.yml:30-36)
  • Signing is keyless Sigstore via GitHub OIDC, recorded in Rekor; no long-lived key exists (source: sign-release.yml:18-22; GOVERNANCE.md:111,123-125)
  • Reproducible binary hash for v6.2.28: 9E89C1E3ABF4DD36560B0B5F3515BDA3DAB8B96F00EF35DA5376A9B60176BD15 (hMailServer.exe 6.2.28.38) with the v145 toolset and the README library layout; the v6.2.27 hash was 8CD745C49657A2E02989A8A86E9290C0AA49D317466B860C3749C757C129410F (6.2.27.37, identical across three clean Release builds) and the v6.2.24 hash 64406163b1a3a8a865e34d74883b5e2a05a5a1c476b500b26745cde67d8a989b (source: release notes' verification sections, gh)
  • The installer is Authenticode-signed from 6.3.1 (11 September 2026) with Azure Artifact Signing, countersigned; SmartScreen still warns on reputation and Sigstore remains the provenance check for every asset; releases before 6.3.1 are unsigned and immutable (source: v6.3.1 body; sign-release.yml authenticode job)
  • SBOMs: SPDX 2.3 JSON and CycloneDX JSON, produced by Syft plus the native-dependency merge (source: sbom.yml:55-85; build/merge-native-dependencies-into-sbom.ps1:4-9)

11. Security policy

  • Supported: 6.2.x yes; < 6.2 no (source: .github/SECURITY.md:5-8)
  • Report privately via https://github.com/Progressiverobot/hmailserver/security/advisories/new; never a public issue/discussion/PR or the forum; fallback: a bare issue asking for a private channel (source: SECURITY.md:12-22)
  • Targets: acknowledgement 5 working days; initial assessment 10 working days; fix for a confirmed vulnerability 90 days from acknowledgement; single maintainer, not contractual (source: SECURITY.md:26-34)
  • Coordinated disclosure on a 90-day timetable; advisory published at fix or 90 days, whichever first; reporter credited unless they decline; no NDA, no bounties; CVE requested through GitHub (source: SECURITY.md:36-47)
  • In scope: RCE/memory corruption in protocol handlers; auth/authz bypass; TLS/crypto (DANE, MTA-STS, DKIM/ARC, certificates); SQL injection; privilege escalation via the service or COM API (source: SECURITY.md:49-58)
  • Out of scope: findings requiring an existing administrator account; missing hardening on a listener deliberately exposed without TLS; scanner output with no demonstrated impact (banners, TLS grades); third-party dependency vulnerabilities with no path from hMailServer (source: SECURITY.md:60-75)
  • Supply chain: SBOM (SPDX + CycloneDX) covering .NET and native deps on every release; committed binaries inventoried, checksummed and verified on every build (source: SECURITY.md:77-82)
  • Issue template routes "Security vulnerability" to the advisory URL (source: .github/ISSUE_TEMPLATE/config.yml:3-5)

12. Governance

  • Single-maintainer project stewarded by Progressive Robot Ltd; Christopher Holloway (@chrisholloway5) holds every role; decisions made in the open on the issue tracker (source: GOVERNANCE.md:13-15,29)
  • Maintainer duties: accepting changes, releasing per RELEASE.md (full suite on the exact shipped binary, signing every asset), security response per SECURITY.md, direction via Roadmap.md, infrastructure/credentials (source: GOVERNANCE.md:31-47)
  • Contributor: anyone who opens a PR; follows CONTRIBUTING.md; no CLA (source: GOVERNANCE.md:49-57)
  • Reporter: SUPPORT.md for defects, SECURITY.md for security; credited unless they decline (source: GOVERNANCE.md:59-66)
  • Decisions: maintainer decides with reasons recorded in the PR/issue/release notes; disagreements on the issue, reopening with a reproduction welcome; scope test is "does it serve people running this as a mail server in production"; compatibility with existing deployments is a standing constraint; security decisions are the maintainer's alone (source: GOVERNANCE.md:71-89)
  • Becoming a maintainer: demonstrated sustained judgement plus willingness to take release and security duties; say so on an issue (source: GOVERNANCE.md:94-100)
  • Critical assets: GitHub repo admin; release signing path (Sigstore keyless, no private key held); "code-signing material - signing the Windows installer"; build environment recipe (VS 2026/v145 + hMailServerLibs); test environment recipe (source: GOVERNANCE.md:108-114). NOTE: the installer is not Authenticode-signed today (SECURITY.md:107), so the "code-signing material" row describes an asset the project does not currently hold - see section 17.
  • Continuity: knowledge bus factor 1; access continuity arranged (credentials in a managed arrangement a trusted person can reach); mitigations: everything scripted and committed, keyless signing, AGPLv3 permits a fork (source: GOVERNANCE.md:130-151)
  • Changing GOVERNANCE.md: by pull request, maintainer decides with a reason (source: GOVERNANCE.md:156-158)
  • Regulatory position: "out of scope as a manufacturer" under the EU Cyber Resilience Act and revised Product Liability Directive, reviewed 12 August 2026, self-assessment not legal advice; premises: AGPLv3, no paid edition/support/hosting, no telemetry, one individual (source: hmailserver/docs/RegulatoryScope.md:1-26, doc)
  • Scorecard is measured and published to the OpenSSF API (publish_results: true, since 5 September 2026); Branch-Protection and Code-Review checks are expected to fail on a single-maintainer repo (source: scorecard.yml:9-15,74-86)

13. Assurance case: claims and how each is backed

Claims (source: ASSURANCE-CASE.md section 2):

  • C1 unauthenticated peer cannot execute code/corrupt memory/crash via mail protocols
  • C2 unauthenticated peer cannot read/modify/delete account mail or authenticate as one
  • C3 credentials at rest not practically recoverable from a stolen database
  • C4 credentials in transit not exposed and never sent to an unverified peer
  • C5 untrusted content cannot alter a query, filesystem path or third-party protocol exchange
  • C6 an authenticated user cannot reach another user's mail or gain admin capability
  • C7 a user can verify the downloaded software is what the project published

Threat model actors A1-A6 (internet stranger, sending MTA, network attacker, authenticated user, malicious upstream DNS/TLSA/MTA, local attacker); administrators are explicitly NOT adversaries (source: ASSURANCE-CASE.md section 3). Trust boundaries B1-B6: TCP/TLS termination, protocol grammar, authentication, parameterised query, external process/filesystem, COM management plane (source: section 4).

Backing, as claimed and as checked in code:

  • Single management seam: everything goes through Server/COM/ (source: ASSURANCE-CASE.md section 5; ARCHITECTURE.md "Patterns that matter", doc)
  • Fail-safe hash default: PreferredHashAlgorithm read from [Settings] with default 4 (PBKDF2); values 0/1/2 are logged and replaced with 4; 3 = SHA256, 5 = Argon2id (source: hmailserver/source/Server/Common/Application/IniFileSettings.cpp:171,187, code)
  • Minimum acceptable scheme: MinimumAcceptedHashAlgorithm, default 0 (source: IniFileSettings.cpp:198; IniFileSettings.h:152, code)
  • Pepper: PasswordPepper string setting, default empty (source: IniFileSettings.cpp:203; IniFileSettings.h:153, code)
  • Argon2id via OpenSSL EVP_KDF in DeriveArgon2id (source: hmailserver/source/Server/Common/Util/Hashing/HashCreator.cpp:61-65, code)
  • Outbound TLS verification: verify_peer | verify_fail_if_no_peer_cert (source: hmailserver/source/Server/Common/TCPIP/TCPConnection.cpp:556,602,609, code); DANE and DNSSEC in Common/TCPIP/DaneVerifier.* and DnssecResolver.*; SslContextInitializer.* present (source: ls Common/TCPIP, code)
  • CSPRNG: RAND_bytes appears in 37 server source files; the only remaining rand() is the MIME part-boundary string at Common/Mime/Mime.cpp:807, matching the document's stated exception (code)
  • SCRAM-SHA-256 referenced in TCPConnection.cpp/.h and PersistentAccount.h; TOTP in Common/BO/Account.cpp/.h (code)
  • Session ceiling: GetClientSessionCeiling() (source: IniFileSettings.h:209, code); DatabaseUnavailableMarker.cpp exists under Common/SQL and is used by DatabaseConnectionManager.cpp, BackupExecuter.cpp, IniFileSettings.cpp (code)
  • Compiler hygiene: /WX, CFG, /GS-DEP-ASLR (see section 4; PE header verified)
  • SMTP smuggling rule (CVE-2023-51764): bare-LF accepted only with "Allow incorrect line endings", and only at buffer end (source: ASSURANCE-CASE.md section 6, doc - not re-verified here; protocol readers should confirm the setting name)
  • Static analysis: "CodeQL runs on every push and pull request" is exactly true for C# only. The C++ job "Analyze (c-cpp)" runs on every push to master, weekly and on dispatch, but not on pull requests (codeql.yml:163); since it moved to the hosted windows-2025-vs2026 image on 5 September 2026 it completes - e.g. run 34179895726 (push, 2026-09-08) "Analyze (c-cpp) (windows-2025-vs2026): success" (source: gh run view <id> --json jobs, gh; Roadmap.md:1368). Before that, every scheduled run since the job was added on 2026-08-13 had been cancelled waiting for a self-hosted runner that was never registered.
  • Fuzzing: libFuzzer harnesses under fuzz/ for MIME only; HTML/iCalendar/vCard not done; the corpus is generated by make-corpus.ps1, not committed (source: fuzz/README.md:4-6,47; hmailserver/docs/Fuzzing.md:10-13). The /EHa + catch (...) crash-oracle problem is documented (Fuzzing.md "Why this waited on the crash oracle").
  • Supply chain (C7): sign-release.yml, verify-binary-provenance.yml, sbom.yml, Dependabot, dependency-review (see section 8)
  • Evidence table names: fuzz/, CodeQL workflow, /WX in hMailServer.vcxproj, regression suite; Server/Common/BO/; HashCreator.cpp, IniFileSettings.cpp; TCPConnection.cpp, SslContextInitializer.cpp; CONTRIBUTING.md SQL rule; .github/workflows/sign-release.yml, verify-binary-provenance.yml, sbom.yml (source: ASSURANCE-CASE.md section 7)
  • Residual risk, stated: memory safety mitigated not guaranteed; native statement coverage not measured (only the .NET Control Panel is); reproducibility shown on one toolchain only; CFG is a mitigation not a boundary; bus factor 1; administrators inside the boundary; operator-chosen weakening (plaintext listeners, legacy hashes) is permitted with a warning (source: ASSURANCE-CASE.md section 8)
  • Review cadence: revisit on any threat-model change and at least yearly (source: ASSURANCE-CASE.md "Reviewing this document")

14. Licence

  • Project licence: GNU Affero General Public License v3, 19 November 2007 text in LICENSE; SPDX identifier used throughout is AGPL-3.0-or-later (source: LICENSE:1-2; every build/*.ps1 header line 2; dco.yml:4, code)
  • README states AGPLv3 and points to hmailserver/docs/Licenses for third-party texts (source: README.md:568-571)
  • Copyright line used on source files: "Copyright (c) 2026 Christopher Holloway / Progressive Robot Ltd and the hMailServer contributors" (source: build/build.ps1:1 and all others, code)
  • Header enforcement: build/add-license-headers.py --check in the "SPDX identifier on every source file" required check (source: style.yml:43-55)
  • Local nit: run today, the check flags hmailserver\source\Server\hMailServer\hMailServer.h (MIDL output) - that file is gitignored (.gitignore:24) so CI never sees it, but the script's SKIP_FILES does not list it (code)
  • Third-party licence texts held: 7-Zip, Boost, Fat Cow Icons, MIME Message encoder analyzer, Microsoft SQL Server Compact Edition, OpenSSL, PostgreSQL interface, RMSPF, String library, ZLib, plus License list.ods (source: ls hmailserver/docs/Licenses, code)
  • Known licence gap: no LGPL-2.1 text for MariaDB Connector/C although twenty committed files and the installer include it; msado28*.tlb redistribution terms not established (source: hmailserver/docs/ThirdPartyBinaries.md "Not yet done", doc; Licenses dir confirms absence, code)

15. Third-party components and committed binaries

  • Out-of-tree (built by the developer or by CI's native-dependencies action, referenced via hMailServerLibs): OpenSSL 4.0.2, Boost 1.92.0, PostgreSQL 18.3 libpq (source: hMailServer.vcxproj:76; README.md:287-336; build/Get-LibraryVersions.ps1)
  • In-tree vendored source (not a committed binary, so outside the manifest): zlib 1.3.1 under hmailserver/source/Server/zlib with its LICENSE, compiled into hMailServer.exe for IMAP COMPRESS=DEFLATE (new in 6.2.28); build/native-dependencies.ps1 lists only openssl/boost/postgresql, so zlib is not yet named in the SBOM (source: hmailserver/source/Server/zlib/zlib.h:40; hMailServer.vcxproj; build/native-dependencies.ps1, code)
  • Manifest hmailserver/docs/third-party-binaries.json: schema hmailserver-third-party-binaries/1, 40 artifacts, fields path/sha256/size/component/version/publisher/license/upstream/authenticode/authenticode_signer/disposition/used_by/why (source: JSON parse, code)
  • Dispositions: retain 13, retain-review 13, remove-duplicate 10, remove-unused 3, retain-generated 1 (source: JSON parse, code; matches ThirdPartyBinaries.md "short version" table)
  • Authenticode: 12 Valid (10 MSVC runtime signed by "Microsoft Windows Software Compatibility Publisher", 2 SQL CE MSIs by "Microsoft Corporation"), 28 NotSigned (source: JSON parse; ThirdPartyBinaries.md "Authenticode")
  • Components: MariaDB Connector/C 3.4.9 (20 files: 10 under libraries/mariadb-connector-c-3.4.9/, 10 byte-identical duplicates under installation/Extras/), MSVC v145 runtime 14.51.36231.0 (10, installation/Microsoft.VC145.CRT/), ADO 2.8 type libraries (2, libraries/msado28/, PE images despite .tlb), SQL Server Compact 4.0 SP1 x64 MSI (Microsoft withdrew the download; this copy is the archive of record), SQL CE x86 MSI (unused), 7za.exe 19.00 (upstream 26.02; refresh candidate), ISC.dll (unattributed, exports CheckPorts(), used by the installer - "should not survive to the next release"), isxdl.dll (declared, never called), atl70.dll (VS .NET 2003 ATL, vestigial), dnsapi.dll (Windows 2000, never loaded), Interop.hMailServer.dll (ours, AGPL) (source: ThirdPartyBinaries.md inventory tables; JSON component counts, code)
  • Policy: a binary may be committed only if listed in the manifest; changing one means changing its entry in the same commit; prefer not committing > vendor-signed original > hash+URL; unattributed binaries not acceptable long-term (source: ThirdPartyBinaries.md "Policy")
  • Update procedure: change file, update sha256/size/version/upstream/authenticode ((Get-FileHash <path> -Algorithm SHA256).Hash.ToLower()), update the doc row, push (source: ThirdPartyBinaries.md "Updating the manifest")
  • Scorecard Binary-Artifacts annotation deliberately NOT added until the 13 remove-* entries are gone (source: ThirdPartyBinaries.md "Relationship to OpenSSF Scorecard")
  • CI does not re-verify Authenticode (Linux runner) (source: ThirdPartyBinaries.md "Not yet done"; verify-binary-provenance.yml:29-33)
  • libraries/ also contains the library build scripts build-openssl.ps1, build-boost.ps1, build-pgsql.ps1 and their shared build-common.ps1, plus asio-overrides, fat_cow_icons, mssqlce3.5, and licence .rtf copies; its README.md still reads "hmailserver-dependencies-v1 ... This repository contains hMailServer external dependencies" (stale text from the separate upstream repo) (source: ls libraries; libraries/README.md:1-4, code)
  • MariaDB Connector/C is loaded at runtime from beside the exe (libmysql.dll) with plugins via MYSQL_PLUGIN_DIR; supports MySQL 8 caching_sha2_password, MariaDB ed25519/gssapi (source: ThirdPartyBinaries.md "Required in the tree"; README.md:101)
  • Interop wrapper regeneration documented in hmailserver/source/Tools/Interop/README.md (see section 3)

16. build/ script inventory (name - one-line purpose, from each file's header)

  • Find-MsBuild.ps1 - shared vswhere-based MSBuild locator (dot-sourced by the build scripts)
  • Get-LibraryVersions.ps1 - report the OpenSSL/Boost/PostgreSQL versions the project is pinned to, parsed from hMailServer.vcxproj (openssl=4.0.2, boost=1.92.0, postgresql=18.3 plus the *_dir folder names); CI uses it for the library cache keys and DLL paths
  • add-license-headers.py - adds a copyright line + SPDX identifier to every source file lacking one; --check reports and exits non-zero; handles BOM, CRLF/LF, shebangs, PowerShell help
  • analyze.ps1 - the static-analysis build: Release x64 rebuilt from scratch with MSVC /analyze, findings summarised by code and file into logs\analyze-warnings.txt (replaces the Release binaries; rebuild before a gate)
  • build.ps1 - MSBuild the server solution (x64, build events off; -Configuration, -Clean)
  • build-tests.ps1 - restore the test packages via the parent test solution, then MSBuild RegressionTests.sln (x64)
  • build-tools.ps1 - dotnet publish the five setup/migration tools into their publish\ folders (not the Control Panel)
  • capture-cp.ps1 - screenshot the running hMailCP window, optionally navigating via UI Automation (-Out, -Nav, -Launch)
  • capture-dialog.ps1 - open the Domain Properties dialog in the running hMailCP and screenshot it
  • check-authz-choke-point.py - fail if any file other than ACLManager and its listed callers names GetPermissionForFolder(, GetUseIMAPACL or GetAclEnforcementEnabled: every folder-access decision stays ACLManager's
  • check-catalogues.py - read each translation catalogue against the English: server literals (log stage names, INI/setting names, INBOX, typed syntax) preserved, page references use the page's translated title, every number kept
  • check-db-scripts.ps1 - build a throwaway SQL CE database from CreateTablesMSSQL.sql and apply every upgrade script using SQLScriptParser's rules
  • check-ini-coverage.py - every [Settings] key IniFileSettings reads is named somewhere in the Control Panel (with an EXEMPT list) and is read through IniFileSettings so the database mirror sees it
  • check-localisation.py - the generated English catalogue Strings.resx matches the L()/F()/N()/{loc:L} marks (--write regenerates it); every language keeps placeholders and Alt keys; the 17 languages in COMPLETE translate every key; files in LOCALISED carry no unmarked caption
  • check-mnemonics.py - every static Control Panel caption (XAML and code-built) carries an Alt-key mnemonic and no two on one view or method share one
  • check-roadmap.ps1 - reconcile Roadmap.md tick boxes against per-section counts and the contents table; exits non-zero on drift
  • check-schema-versions.ps1 - prove the schema upgrade chain is contiguous, forward-only and complete for every backend (pwsh 7)
  • find-stale-roadmap-rows.py - flag not-started roadmap rows that name symbols present in the tree (advisory, not a gate)
  • generate-public-suffix-list.ps1 - regenerate Common/AntiSpam/DMARC/PublicSuffixListData.h from the Public Suffix List (-ListFile for a reproducible local copy)
  • generate-settings-index.ps1 - regenerate the Control Panel's Ctrl+K settings search index SettingsSearchIndex.g.cs (CI verifies it is up to date)
  • get-dotnet-runtime.ps1 - download the .NET Desktop Runtime installer the hMailServer installer bundles (-Channel 10.0)
  • hmconfig.ps1 - export/diff/apply hMailServer configuration as JSON over the COM API; apply is a dry run without -Force, deletions need -AllowDelete too; passwords never exported
  • make-app-icon.ps1 - generate the Control Panel .ico (256/64/32/16 px)
  • make-hmailserver-writable.ps1 - grant Everyone Modify on the hMailServer install folders (elevates; test machines only)
  • make-installer-art.ps1 - generate the Inno Setup wizard bitmaps (164x314 and 55x55)
  • merge-native-dependencies-into-sbom.ps1 - add OpenSSL/Boost/libpq to a Syft SBOM (SPDX 2.3 or CycloneDX) in place, idempotent by PURL
  • native-dependencies.ps1 - emit the native dependencies as JSON, versions parsed from hMailServer.vcxproj include paths
  • post-build.ps1 - elevated wrapper for post-build.bat: copy runtime DLLs and the .tlb, register the COM server/service
  • preflight-tests.ps1 - verify the machine is in the known-good state for the regression suite; -Clean removes a stale ERROR log
  • regenerate-interop.ps1 - TlbImp the Release type library into Interop.hMailServer.dll and update its manifest hash/size
  • register-com.ps1 - elevated re-registration of the type library and interfaces (needed only when the IDL adds a NEW interface)
  • run-tests.ps1 - run the NUnit regression suite against the live service (-StopOnError, -Where)
  • upgrade-test-database.ps1 - apply pending schema upgrade scripts to the bench database through the running service's COM API (source: header comments of each file under build/, code)

17. Contradictions between documents and code (found while checking)

  • RELEASE.md line 59 and hmailserver/source/Tools/Interop/README.md line 15 contain a raw carriage-return byte where a backslash should be: the bytes are build<CR>egenerate-interop.ps1, and RELEASE.md line 60 has hmailserver\docs<TAB>hird-party-binaries.json with a raw TAB. grep -rF 'build\regenerate-interop' --include=*.md finds nothing. The intended text is build\regenerate-interop.ps1 and hmailserver\docs\third-party-binaries.json (source: byte inspection of both files, code)
  • README.md:110 says "Schema | Database version 6027"; the code requires 6031 (Constants.h:173)
  • Dated test-count comment: run-tests.ps1:9 names "1361" and adds "(the count then; over 2,000 now)"; the actual [Test] count is 2,127 at v6.2.28 and the v6.2.28 gate was 2,127 tests, 2,119 passed, 8 skipped (the old ci.yml and server-build.yml comments have been removed)
  • GOVERNANCE.md:112 lists "Code-signing material - Signing the Windows installer" as a critical asset; since 6.3.1 that is the Azure Artifact Signing account, its identity validation and the app registration CI signs through, not a certificate file (there is none to hold)
  • ASSURANCE-CASE.md section 6 says "CodeQL runs on every push and pull request"; codeql.yml skips the C++ job on pull requests (it runs on push to master, weekly and on dispatch, and completes on the hosted image since 5 September 2026)
  • ASSURANCE-CASE.md section 6 says /GS, /NXCOMPAT, /DYNAMICBASE are "active"; the vcxproj does not set them (MSVC defaults). The shipped binary does have DEP/ASLR/HEVA/CFG on and a load-config directory, so the claim is true in effect but not project-enforced
  • CONTRIBUTING.md:46-48 says a PR whose commits lack the trailer "fails the DCO check"; the check is skipped for OWNER/MEMBER/dependabot authors and is not a required status check, so it cannot block a merge on its own (dco.yml:39-42; ruleset)
  • RELEASE.md:76 cites "Windows SDK 10.0.26100" as part of the reproducibility toolchain; the project pins WindowsTargetPlatformVersion to 10.0 (latest installed), so the SDK is not fixed by the source
  • ASSURANCE-CASE.md section 6 describes the fuzz suite as having "a corpus, dictionaries and a regression directory"; fuzz/README.md:47 says corpus\ is generated, not committed (one dictionary file, dict/mime.dict)
  • ThirdPartyBinaries.md's "Twenty MariaDB Connector/C DLLs" and "40 committed binaries" both agree with the JSON manifest (20 MariaDB entries, 40 artifacts) - no contradiction, recorded as checked
  • README.md:180 requires "An installed version of hMailServer 5.7 or later" to build - the regression environment doc requires the service to run from the repo build, NOT an installed copy (RegressionEnvironment.md:35-41)

18. Unconfirmed (recorded, not verified)

  • Whether the v6.2.24 installer is (un)signed with Authenticode was not checked against the asset itself; the statement is from SECURITY.md and sign-release.yml
  • The SMTP bare-LF / "Allow incorrect line endings" setting name and behaviour (ASSURANCE-CASE.md section 6) - not grepped here; belongs to the SMTP reader
  • Whether the OpenSSF Best Practices "Passing 100%" also covers Silver/Gold criteria - the page reports Passing only
  • Why the three scheduled C++ CodeQL runs were cancelled (runner offline vs manual) - only the outcome was read
  • Whether libraries/mssqlce3.5 is used by anything (not referenced in the manifest doc's inventory)
  • The Roadmap.md item total (RELEASE.md:31 and check-roadmap.ps1 say "750"; not counted here)
  • Whether a second person currently holds any repository rights (GOVERNANCE.md says no; not checked via API)

Clone this wiki locally