Skip to content

Project Handbook

chrisholloway5 edited this page Sep 8, 2026 · 34 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.

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.27, build 37: HMAILSERVER_VERSION "6.2.27", HMAILSERVER_VERSION_NUMERIC 6,2,27,37, HMAILSERVER_BUILD 37 (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.27, OutputBaseFilename=hMailServer-6.2.27-x64, VersionInfoVersion=6.2.27.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.27, published 2026-09-07T01:12:26Z, not draft, not prerelease, six assets: hMailServer-6.2.27-x64.exe, hmailserver.spdx.json, hmailserver.cyclonedx.json, each with a .cosign.bundle (source: gh release view v6.2.27 --json assets, gh). v6.2.25 and v6.2.26 were published 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 (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.27) 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)

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.27 binary is hMailServer.exe 6.2.27.37, SHA-256 8CD745C49657A2E02989A8A86E9290C0AA49D317466B860C3749C757C129410F, byte-identical across three clean Release builds; the installer hMailServer-6.2.27-x64.exe is 77,237,885 bytes, SHA-256 F60D3C30F27AF9EDC18BEE295DA231C666E826C1436E2214593B33499227C1A6 (source: v6.2.27 release notes, verification section, 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,124 [Test] attributes across the suite at HEAD (2,024 at v6.2.27); RegressionTests.csproj lists 347 <Compile Include> entries explicitly (no glob) (source: grep -c "^\s*\[Test\]" over hmailserver/test/RegressionTests; csproj, code). The v6.2.27 gate was 2024 tests, 2017 passed, 0 failed, the 7 explicit skips, on the assertion build and again on the shipping binary; the Control Panel's tests passed (no count published) (source: v6.2.27 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)

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; in the tree after 6.2.27, not yet in a published release), 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; they landed after v6.2.27 (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, in the tree after 6.2.27) 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

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.27: 8CD745C49657A2E02989A8A86E9290C0AA49D317466B860C3749C757C129410F (hMailServer.exe 6.2.27.37, identical across three clean Release builds) with the v145 toolset and the README library layout; the v6.2.24 hash was 64406163b1a3a8a865e34d74883b5e2a05a5a1c476b500b26745cde67d8a989b (source: release notes' verification sections, gh)
  • The installer is NOT Authenticode-signed; SmartScreen/UAC do not read Sigstore signatures; Azure Trusted Signing is named as the realistic route (source: SECURITY.md:106-107; sign-release.yml:24-28)
  • 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 (in the tree after 6.2.27, not yet in a published release); 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)
  • Stale test-count comment: run-tests.ps1:9 "1361"; actual [Test] count 2,124 at HEAD and the v6.2.27 gate was 2024 tests, 2017 passed, 7 explicit skips (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; SECURITY.md:106-107 and sign-release.yml:24-28 say the installer is not Authenticode-signed and the project has no certificate
  • 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)

Home — full index

Start here

1. Install and run

2. Secure it

3. Operate it

4. Extend it

5. Contribute to it

Look it up — from any journey


About this wiki

Clone this wiki locally