Skip to content

Control Panel Reference

chrisholloway5 edited this page Sep 10, 2026 · 10 revisions

Control Panel Reference

How hMailCP connects, its navigation map, and every page and dialog with the setting behind each field, checked against the Control Panel's source. 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.

All paths below are relative to hmailserver/source/Tools/ControlPanel/ unless stated. "IDL" = hmailserver/source/Server/hMailServer/hMailServer.idl. "IniFileSettings.cpp" = hmailserver/source/Server/Common/Application/IniFileSettings.cpp. Line numbers were read on 2026-09-04 at commit 4d5da3f (branch server-fixes-wave) and are exact where given as :N, approximate where given as ~N. Corrections made on 2026-09-08 cite the tree at commit 40ae9491d (master HEAD); anything present there but in no release before v6.2.28 (2026-09-08) is marked "new in 6.2.28"; the HttpProxy rows cite the tree v6.2.28 was cut from, one commit later. The diagrams, structural tables and counts added on 8 September 2026 were read from the working tree at commit f088336c2 - the v6.2.28 tag itself - with no uncommitted change anywhere under hmailserver/source/Tools/ControlPanel, so every Control Panel fact on this page is a shipped one and nothing here is unreleased. One thing changed in v6.3.0 (2026-09-10), which is the current release: the sign-in box's user name is a literal again rather than a translated caption, so choosing Chinese no longer puts 管理员 where the account name Administrator belongs and refuses the credential it has just asked for (#156, #177; Views/ConnectView.xaml, Views/UtilityViews.cs and all eighteen Strings.*.resx). Twenty-one files under hmailserver/source/Tools/ControlPanel differ between the two tags: those twenty, and ControlPanel.csproj, whose <Version> is the release stamp. Nothing else moved.

1. What it is

  • Assembly name hMailCP (hMailCP.exe), target net10.0-windows, WPF, x64 only, version 6.2.28 (the csproj is stamped with each release), product "hMailServer Control Panel", company Progressive Robot Ltd (source: ControlPanel.csproj:5-8, 15, 19, 39-40)
  • Not self-contained; the installer bundles the .NET 10 Desktop Runtime (source: ControlPanel.csproj:38-39 comment; README.md:130)
  • UI framework WPF-UI 4.3.0 (Fluent), charts LiveChartsCore.SkiaSharpView.WPF 2.0.5, QR codes QRCoder 1.8.0, plus System.Management and System.ServiceProcess.ServiceController (both 10.0.11); DPAPI comes from the .NET runtime's ProtectedData class, not a package (source: ControlPanel.csproj:52-56; Services/Totp.cs:223)
  • Solution Tools/ControlPanel.sln holds three projects: ControlPanel (WPF), ControlPanel.Core (WPF-free logic: navigation map, palette search, setting claims, pipelines), ControlPanel.Tests (source: ls Tools/; NavigationMap.cs class summary "the tests reference ControlPanel.Core, which has no WPF")
  • Test files exist for: AccessibleNames, ApiKeyStore, ChartDataTable, ChartPalette, ColourVision, DirectorySyncReport, HostReachability, ListenerProbe, Loc, MessageStoreConsistencyReport, MnemonicText, NavigationMap, NumericField, PageSplit, PaletteSearch, PaletteUsage, PasswordGenerator, PasswordStrength, SearchTerms, SettingClaims, SettingsSearchIndex, SpamPipeline, StatusSemantics, TlsPosture, VirusPipeline, WelcomeIntents, WindowsServiceInfo (source: ls Tools/ControlPanel.Tests/Services)
  • Installer: Start Menu shortcut "hMailServer Control Panel" -> {app}\ControlPanel\hMailCP.exe, component controlpanel (source: hmailserver/installation/section_icons.iss:3); post-install checkbox "Run hMailServer Control Panel" (source: section_run.iss:9)
  • The Control Panel does NOT need the admintools (COM type library) component - it binds late through IDispatch (source: README.md:146; ServerSession.cs:11-14 "late binding (IDispatch), so no interop assembly is required")
  • Main window title "hMailServer Control Panel", default 1280x800, minimum 760x520, Mica backdrop, content extends into title bar (source: MainWindow.xaml:9-15)
  • Command line: hMailCP.exe /connect <host> <user> <password> connects automatically on load (source: MainWindow.xaml.cs constructor ~88-101, comment "Optional auto-connect")
  • Per-user state lives in HKCU\Software\hMailServer\ControlPanel: values WindowWidth, WindowHeight, WindowLeft, WindowTop, WindowMaximized ("1"/"0"), Theme ("Light"/"Dark"), PaletteUsage and - new in 6.2.28 - Language (a BCP 47 tag such as sv or pt-BR; empty = the Windows display language) (source: MainWindow.xaml.cs:29, 807-855; Services/LanguageChoice.cs:26-27, 47-48, 100-101)
  • Theme follows the OS until the toggle button (bottom of the sidebar, sun/moon icon) is pressed; the choice is then stored (source: MainWindow.xaml.cs ApplySavedTheme, Theme_Click, UpdateThemeToggle_)
  • Localisation (new in 6.2.28): every caption is written once in English and marked where it is used - L("...") translates, F("...", args) translates and formats, N("...") only marks (the navigation map keeps its English titles and translates them where shown) in C#, {loc:L '...'} in XAML - and the English text is the key into Resources/Strings.<culture>.resx; a missing entry falls back to English. Seventeen languages ship, each named in itself in the picker: English, Čeština (cs), Dansk (da), Deutsch (de), Español (es), Suomi (fi), Français (fr), Italiano (it), 日本語 (ja), Norsk bokmål (nb), Nederlands (nl), Polski (pl), Português (Brasil) (pt-BR), Русский (ru), Svenska (sv), Türkçe (tr), Українська (uk), 简体中文 (zh-Hans), plus "Windows display language" (the default: CultureInfo.InstalledUICulture; an English or unknown culture means untranslated English). The choice is per Windows user (HKCU\Software\hMailServer\ControlPanel\Language), offered on the sign-in card's "Language" combo and by the globe button ("Change the language") next to the theme toggle in the sidebar footer, and changing it restarts the Control Panel after a Yes/No prompt. Resources/Strings.resx (English) is generated from the source by build/check-localisation.py, which fails CI when it is behind, when any of the seventeen catalogues (all listed as COMPLETE) lacks a key, or when a translation loses a {n} placeholder or an Alt-key underscore; build/check-catalogues.py checks that server-side literals, page titles and numbers survive translation; build/check-mnemonics.py checks every catalogue's translated captions for a unique key per scope. The palette matches a query against the translated outcome phrases as well as the English ones (source: Services/Loc.cs:54-75, 128-165; Services/LanguageChoice.cs; Views/ConnectView.xaml:59-72; MainWindow.xaml:212-217; MainWindow.xaml.cs:966-989; build/check-localisation.py; build/check-catalogues.py; build/check-mnemonics.py)
  • The Control Panel's own error log: %LOCALAPPDATA%\hMailServer\ControlPanel\control-panel-errors.log; an unhandled UI exception shows a dialog "An unexpected error occurred..." offering to restart the app (source: App.xaml.cs:28-81)
  • About page reports "Version {assembly version, 3 parts} - .NET {runtime}", "Connected server: hMailServer {Application.Version} @ {host}", and states the panel "is built with WPF-UI (Fluent design) and LiveCharts2 on .NET 10" (source: Views/AboutView.cs:43-44, 85, 183-185)

1.1 How the three projects fit together, and what is generated

flowchart TB
    SRC["FeatureSettingsView.xaml.cs<br/>ServerSettingsView.xaml.cs<br/>BackupView and IPRangesView"]
    GEN["build/generate-settings-index.ps1<br/>run by hand, re-run and verified in CI"]
    IDX["Services/SettingsSearchIndex.g.cs<br/>389 committed entries"]
    CORE["ControlPanel.Core, hMailCP.Core.dll<br/>NavigationMap, PaletteSearch, IntentIndex, SettingClaims,<br/>SpamPipeline, VirusPipeline, TlsPosture, StatusSemantics,<br/>ApiKeyStore, MnemonicText, Loc. No WPF."]
    CP["ControlPanel, hMailCP.exe<br/>14 XAML views, plus 43 further files of<br/>code-built views, dialogs and shared controls"]
    TESTS["ControlPanel.Tests<br/>27 test files, referencing Core only"]
    RESX["Resources/Strings.resx and 17 catalogues<br/>3,376 texts in each"]
    CHECK["build/check-localisation.py<br/>build/check-catalogues.py<br/>build/check-mnemonics.py"]
    SRC --> GEN --> IDX --> CORE
    CORE --> CP
    CORE --> TESTS
    RESX --> CP
    CHECK -.-> RESX
    CHECK -.-> SRC
Loading
Piece What it is Why it is separate
ControlPanel.Core (hMailCP.Core.dll) The information architecture, the searches, the judgements the three overview pages print, and the localisation lookup The structure is the part worth testing, and the test assembly must not acquire WPF, COM and System.ServiceProcess in order to read a table
ControlPanel (hMailCP.exe) The Fluent shell and every view
ControlPanel.Tests 27 test files (source: ls Tools/ControlPanel.Tests/Services) Holds the navigation map, the palette, the pipelines, the claims and the accessible names to their contracts
Services/SettingsSearchIndex.g.cs 389 entries: label, INI key or COM path, owning page Committed rather than produced at build time, so a change to what is searchable appears in a review diff. CI re-runs the generator and fails if the working tree moves; SettingsSearchIndexTests fails if a setting or a whole page is missing (source: build/generate-settings-index.ps1 header)

Of the 389 entries, 367 come from the two data-driven settings views and 22 from the hand-written Backup (14) and IP ranges (8) pages. The five other hand-written pages the generator scans - Domains, Rules, Routes, TCP/IP ports and SSL certificates - contribute none, because their editors are grid cells and dialog fields rather than a named control bound to one setting; those pages are reachable in the palette by title, alias and outcome phrase, not by setting name (source: grep -c 'new SettingEntry(' Services/SettingsSearchIndex.g.cs = 389, counted per page).

2. Connecting to the server (ConnectView + ServerSession)

  • The connect card is titled "Connect to server" with subtitle "Sign in with the hMailServer administrator credential."; fields: Server (pre-filled localhost), User name (pre-filled Administrator), Password; button "Connect"; link "Set up two-factor authentication…"; below it (new in 6.2.28) a "Language" combo listing the seventeen catalogues plus "Windows display language" - choosing one restarts the Control Panel in that language (source: Views/ConnectView.xaml:19-72)
  • Enter in any field submits; a second Connect while one is in progress is ignored (source: Views/ConnectView.xaml.cs:23-35)
  • An empty host is normalised to localhost (source: Services/ServerSession.cs Connect(): normalizedHost)
  • Before touching COM, HostReachability.CheckAsync probes TCP port 135 (RPC endpoint mapper) on the target with a 4-second default timeout, off the UI thread, so an unreachable host fails in seconds instead of the DCOM RPC timeout (source: Services/HostReachability.cs:55, 62; Views/ConnectView.xaml.cs:44-57)
  • COM activation: Type.GetTypeFromProgID("hMailServer.Application") for a local host, Type.GetTypeFromProgID("hMailServer.Application", host) (DCOM) for a remote one; then Authenticate(user, password) must return a non-null account (source: Services/ServerSession.cs Open() ~401-441)
  • "Local" is decided by LocalHostNames.IsLocal(host) (source: ServerSession.cs IsLocalHost; Services/LocalHostNames.cs)
  • Error strings: "hMailServer COM API is not registered on the target machine." / "Authentication failed. Check the user name and password." / "Unable to reach the server (RPC unavailable)." (HRESULT -2147023174) (source: ServerSession.cs Open())
  • The password is kept in memory DPAPI-protected (ProtectedSecret) so the session can re-authenticate after a service restart without prompting (source: ServerSession.cs Connect(); Services/ProtectedSecret.cs)
  • Self-healing link: every read of ServerSession.Application re-verifies the link at most once per 2 s by reading ServerState (the value is ignored - a paused engine still counts as alive); on a transport failure it reconnects with up to 5 attempts 300 ms apart, never more often than every 3 s, and only if the local hMailServer Windows service is Running or StartPending - because activating the COM class would START a stopped service (source: ServerSession.cs:31-38, VerifiedApplication(), ServiceLooksRunning(), IsAlive())
  • On a reconnect after a restart the link is adopted only once ServerState == 3 (running) unless it is the last attempt (source: ServerSession.cs Reconnect(), IsServerReady(); StateRunning = 3 at :29)
  • Transport-failure HRESULTs treated as "link gone": 0x800706BA, 0x800706BE, 0x800706BF, 0x800706B5, 0x80010108, 0x80010105, 0x800401FD, 0x80080005, plus InvalidComObjectException (source: ServerSession.cs IsTransportFailure())
  • After a self-heal the shell shows a toast "Reconnected to {host} after the service restarted." (title "Connection restored") and re-enters the current page (source: MainWindow.xaml.cs OnSessionReconnected)
  • DescribeComError maps: transport failure -> "the connection to the hMailServer service was lost - it was most likely restarted. The Control Panel will reconnect by itself; press Reload to try again."; message containing "connection to the database" -> "the server cannot reach its database..."; "do not have access" -> "you must connect with the hMailServer server-administrator account to view or change these settings." (source: ServerSession.cs DescribeComError())
  • Status snapshot read for dashboard/status/queue: Application.Status ProcessedMessages, RemovedSpamMessages, RemovedViruses, SessionCount[1/3/5] (SMTP/POP3/IMAP per IDL eSessionType), StartTime, UndeliveredMessages (tab-separated rows) (source: ServerSession.cs:24-27, ReadStatus())
  • Once connected: sidebar footer shows a green dot and "{user} @ {host}" (accessible name "Connected to {host} as {user}") and "hMailServer {Application.Version}"; navigation and search are enabled; the app navigates to the Dashboard (source: MainWindow.xaml:194-201; MainWindow.xaml.cs OnConnected() -> NavigateTo("dashboard"))
  • Connecting also wires IniFeatureStore.ComReadSetting/ComWriteSetting to Settings.GetIniSetting/Settings.SetIniSetting, so [Settings] INI values can be edited on a remote server (source: ServerSession.cs SetCurrent() :66-104; IDL:735-739 GetIniSetting/SetIniSetting/DeleteIniSetting)

2.0 Local and remote, in one picture

flowchart LR
    subgraph LOCAL["Control Panel running ON the server"]
        L1["hMailCP.exe"]
    end
    subgraph REMOTE["Control Panel running on another machine"]
        R1["hMailCP.exe"]
    end
    SVC["hMailServer service<br/>the COM class lives inside this process"]
    FILES["hMailServer.INI<br/>hMailServerApiKeys.ini<br/>the log folder<br/>certificate and key files<br/>the Service Control Manager<br/>the local TCP listener table"]
    L1 -->|"in-process activation, Type.GetTypeFromProgID"| SVC
    R1 -->|"DCOM over RPC, after a TCP 135 probe, GetTypeFromProgID with the host"| SVC
    L1 -->|"direct, and preferred over COM wherever both exist"| FILES
    R1 -.->|"no channel at all: pages degrade, see 6.2"| FILES
Loading

2.1 The sign-in, in order

sequenceDiagram
    autonumber
    participant V as ConnectView
    participant HR as HostReachability
    participant SS as ServerSession
    participant COM as hMailServer.Application
    V->>V: lock the fields, swap the button for a progress ring
    V->>HR: CheckAsync host, 4 second budget, off the UI thread
    Note over HR: LocalHostNames.IsLocal short-circuits to Ok, loopback is never probed
    HR->>HR: resolve the name, then TCP connect to port 135 on every address it returns
    HR-->>V: Reachable, or one sentence naming what to check
    V->>V: await Task.Delay 50, so the busy state actually paints
    V->>SS: Connect host, user, password
    SS->>COM: GetTypeFromProgID, local form or remote form
    SS->>COM: Authenticate
    alt an account comes back
        COM-->>SS: IInterfaceAccount
        SS->>SS: release the account, keep the Application
        SS->>SS: keep the password as a ProtectedSecret
        SS-->>V: true
        V->>V: ServerSession.SetCurrent, then OnConnected
    else nothing comes back
        SS->>COM: read AdministratorTOTPEnabled
        SS-->>V: false, with SecondFactorRequired set when a factor is enrolled
        V->>V: TotpPromptDialog once, then Connect again with the code
    end
Loading

Everything after the reachability probe runs on the UI thread deliberately: an out-of-process COM proxy belongs to the apartment that created it, and this is the apartment that has to keep using it for the rest of the session. Moving the activation to a worker thread orphans the object; the correct version of that fix is a long-lived STA thread owning the session with every call marshalled onto it, which is a rework and not a progress ring (source: HostReachability.cs class comment; ConnectView.xaml.cs:60-66).

COM sign-in is outside both the per-IP auto-ban and the per-name account lockout, on purpose: guessing an administrator's mailbox name over IMAP, POP3 or SMTP - where the lockout does apply - must not be able to lock that administrator out of the tool they would use to respond. DCOM access is itself an authenticated Windows privilege, not an anonymous mail port (source: COM/COMAuthentication.cpp:47-58).

2.2 The self-healing link, as a state machine

stateDiagram-v2
    [*] --> Disconnected
    Disconnected --> Connected: Authenticate returned an account
    Connected --> Connected: read again within 2 s, no probe sent
    Connected --> Probing: more than 2 s since the last check
    Probing --> Connected: ServerState answered, whatever it answered
    Probing --> Broken: a transport HRESULT came back
    Broken --> Cooling: fewer than 3 s since the last repair attempt
    Cooling --> Broken: the next read
    Broken --> Held: local hMailServer service is not Running or StartPending
    Held --> Broken: the next read, once the service is up again
    Broken --> Healing: service looks alive and 3 s have passed
    Healing --> Connected: re-authenticated, and ServerState is 3
    Healing --> Broken: 5 attempts, 300 ms apart, all failed
Loading

Three details in that machine are load-bearing:

  • The probe asks for ServerState, not Version. A shutting-down service keeps answering Version from a static string long after it has closed its database, so a Version probe reports a healthy link while every real read fails with "no connection to the database". Only that the call answered is the liveness signal: a paused engine reports Stopped while the service, the COM object and the link are all healthy, and treating that as death once put the panel into a permanent false-reconnect loop - a toast and a second of frozen UI every few seconds for as long as the pause lasted.
  • A stopped service is never healed. The COM class is registered against the service, so activating it would start a service the administrator deliberately stopped.
  • Readiness is a different question from liveness. On a reconnect the new link is adopted only once ServerState == 3, except on the last attempt, because a still-starting server is more useful than none. The service reports "running" to the SCM well before hMailServer has finished starting, and authenticating as the administrator only reads the INI file - so both succeed while the database connection is still coming up (source: ServerSession.cs IsAlive(), IsServerReady(), VerifiedApplication(), Reconnect()).
HRESULT Windows name Read as
0x800706BA RPC_S_SERVER_UNAVAILABLE the link has gone - and, at sign-in, the message "Unable to reach the server (RPC unavailable)."
0x800706BE RPC_S_CALL_FAILED the link has gone
0x800706BF RPC_S_CALL_FAILED_DNE the link has gone
0x800706B5 RPC_S_UNKNOWN_IF the link has gone
0x80010108 RPC_E_DISCONNECTED the link has gone
0x80010105 RPC_E_SERVERFAULT the link has gone
0x800401FD CO_E_OBJNOTCONNECTED the link has gone
0x80080005 CO_E_SERVER_EXEC_FAILURE the link has gone
InvalidComObjectException not an HRESULT the link has gone - the proxy this session released after a heal
anything else the server answering, so the link stays up. Most often "you do not have access", which means this session is not a server administrator, and tearing the link down for it would be wrong

3. Administrator two-factor authentication (TOTP)

  • Secret stored under HKLM\SOFTWARE\hMailServer, value AdminTotpSecret, as base64 of a machine-scope DPAPI blob (no entropy) - the same value the retired hMailServer Administrator used, so an existing 2FA setup carries over (source: Services/Totp.cs:3-7, 166-167, 212-241)
  • RFC 6238 TOTP: HMAC-SHA1, 30-second period, 6 digits, accepted window +/-1 step (source: Totp.cs:17-21, 49-57)
  • Prompt appears after a successful COM authentication whenever a secret is configured; 3 attempts, then back to the Connect screen; Cancel also returns to Connect (source: MainWindow.xaml.cs VerifyTwoFactor(), OnConnected())
  • Prompt dialog title "Two-factor authentication", text "Enter the 6-digit code from your authenticator app:" (source: Views/TotpPromptDialog.cs:30, 40)
  • Setup dialog title "Two-factor authentication setup"; shows a QR code (QRCoder) for otpauth://totp/hMailServer%20Control%20Panel?secret=<base32>&issuer=hMailServer&digits=6&period=30 and the base32 key in 4-char groups; enabling or disabling requires a valid current code; button reads "Enable two-factor authentication" / "Disable two-factor authentication" (source: Views/TotpSetupDialog.cs:65, 159-176, 208-236; Totp.cs:60-65)
  • Writing the secret needs local administrator rights (HKLM); the dialog reports "Changing two-factor authentication settings requires administrator rights. Restart the Control Panel as an administrator and try again." on UnauthorizedAccessException (source: TotpSetupDialog.cs:239-242)
  • Reachable from the Connect screen link and from Access & abuse protection > Administrative access > "Two-factor" tab (button "Set up or turn off two-factor authentication…") (source: ConnectView.xaml:54; Views/ServerSettingsView.xaml.cs BuildAdminAccess ~2700-2735)
  • Applies only to Control Panel sign-in, not to the REST API or COM scripts (source: ServerSettingsView.xaml.cs BuildAdminAccess card blurb "Two-factor authentication")
  • Server-enforced administrator second factor (added 5 Sep 2026), the recommended one: enrol/remove from Access & abuse protection > Administrative access > "Two-factor" tab, card "Second factor on the administrator credential (recommended)" (button "Set up or turn off the server-enforced second factor"), dialog AdministratorTwoFactorDialog. The secret is the server's (hMailServer.ini [Security] AdministratorTotpSecret, DPAPI-protected), enrolled via COM Settings.EnrolAdministratorTOTP; the dialog confirms with a code and rolls the enrolment back if you close without confirming. Once enrolled, EVERY client of the administrator credential needs the code: the connect screen prompts for it (ServerSession.Connect(host,user,password,code) after a password-only attempt sets SecondFactorRequired), COM callers use AuthenticateWithCode, the REST API uses an X-hMailServer-OTP header. The old HKLM AdminTotpSecret card is retained as "Two-factor for this Control Panel only" and relabelled the weaker option (source: ServerSettingsView.xaml.cs BuildAdminAccess; Views/AdministratorTwoFactorDialog.cs; Services/ServerSession.cs; Views/ConnectView.xaml.cs)
  • Per-ACCOUNT (mailbox) two-factor is a separate feature on the Account dialog "Two-factor" tab, with app passwords on the "App passwords" tab (see section 7.2)

3.1 The two second factors, side by side

Server-enforced (recommended) This Control Panel only
Secret hMailServer.ini [Security] AdministratorTotpSecret, DPAPI-protected HKLM\SOFTWARE\hMailServer, value AdminTotpSecret, base64 of a machine-scope DPAPI blob with no entropy
Enrolled from Access & abuse protection > Administrative access > Two-factor, card "Second factor on the administrator credential (recommended)" -> AdministratorTwoFactorDialog -> COM Settings.EnrolAdministratorTOTP The Connect screen link, or the same tab's second card -> TotpSetupDialog
Checked by the server, before it returns an account at all the Control Panel, after the server has already accepted the password
Protects the Control Panel (AuthenticateWithCode), COM scripts, and the REST API (X-hMailServer-OTP header) this installation of the Control Panel, on this machine, and nothing else
Rights needed to enrol server administrator local administrator, because it writes HKLM
Wrong code back to the Connect screen; the prompt is offered once per attempt because a wrong password sets the same flag three attempts, then back to the Connect screen
Silent reconnect after a service restart not possible - a silent reconnect cannot produce a fresh code, so the link stays broken and the user signs in again through the prompt unaffected; the prompt only ever appears at sign-in
Half-finished enrolment rolled back if the dialog is closed without confirming a code the button will not enable without a valid current code
Algorithm RFC 6238, HMAC-SHA1, 30-second period, 6 digits, +/-1 step the same

4. Shell: navigation tree, palette, breadcrumb, page lifecycle

  • The sidebar tree is built from Services/NavigationMap.cs (data, WPF-free, tested); MainWindow contains no page names (source: MainWindow.xaml.cs BuildNavTree() comment; MainWindow.xaml:96-98)

  • Groups are expanded by default and carry a Fluent icon; every node's tool tip is "{Title} - {Purpose}"; automation ids nav-<key> for pages and navgroup-<slug> for groups (source: MainWindow.xaml.cs BuildNavItem())

  • Search: a "Search Ctrl+K" button above the tree and the Ctrl+K shortcut open the command palette; both only work while connected (source: MainWindow.xaml:75-94; MainWindow.xaml.cs PreviewKeyDown, ShowPalette())

  • Palette placeholder: Search, or say what you want to do - "stop spam", "block an IP"; footer "↑↓ move Enter open Esc close"; no-match hint suggests an outcome phrase, a page name, or an hMailServer.INI key (source: Views/NavigationPalette.cs:105, 150, 189-190)

  • Palette result sections: "Recently visited", "Most used", "Tasks", "Pages", "Settings" (source: Services/PaletteSearch.cs:97-101)

  • The palette searches page titles + aliases + legacy paths, outcome phrases from Services/IntentIndex.cs (e.g. "stop spam" -> antispam, "block an ip" -> ipranges, "someone is trying to guess passwords" -> autoban, "use microsoft 365 or google sign-in" -> authentication), and the generated settings index of 389 entries (label, INI key or COM path, owning page) produced by build/generate-settings-index.ps1 - 347 at 6.2.24 (source: PaletteSearch.cs class summary; IntentIndex.cs:67-227; Services/SettingsSearchIndex.g.cs header and grep -c 'new SettingEntry(' = 389)

  • Every palette result names the page it lives on ("in {group} > {page}" / "Opens {page}") (source: NavigationPalette.cs:25-31, Describe())

  • Breadcrumb bar above the content: group segments are buttons that reveal the group in the tree; up to three "Related:" links from the page's SeeAlso list; a "Found by search" marker after a palette jump (source: MainWindow.xaml:228-253; MainWindow.xaml.cs UpdateBreadcrumb(), GroupSegment(), RelatedLink(), SearchMarker())

  • Pages are created once per session and cached; each implements IPageLifecycle.OnEnter/OnLeave; an OnEnter exception is shown as a toast "Could not load this page: ..." (title "Server unavailable") rather than crashing (source: MainWindow.xaml.cs NavTree_SelectedItemChanged(), EnterPage(), interface at end of file)

  • "Most used" counts only pages the user chose, not the automatic Dashboard navigation at connect (source: MainWindow.xaml.cs NavTree_SelectedItemChanged() automaticNavigation_)

  • Welcome page tiles call MainWindow.NavigateTo(key); External setup, Dashboard and the overview pages navigate the same way (source: Views/WelcomeView.cs:105; Views/ExternalSetupView.cs:348)

  • Alt-key access mnemonics (5 September 2026): every static caption carries one - 372 captions today (369 at 6.2.24) across the 14 XAML views and 36 code-built views and dialogs, 340 with a key and 32 exempt (Cancel/OK/Close answer to Escape and Enter, the "…" browse buttons, and captions repeated per row, card, record or generated setting, which are reached with the arrow keys). Buttons and check boxes read the underscore through the Wpf.Ui templates (RecognizesAccessKey); the TextBlock above an editor goes through Views/Mnemonic.cs, which underlines the letter, registers the key with AccessKeyManager for the editor, answers the manager's AccessKeyPressed question for it (a bare text box does not and is skipped) and sets AutomationProperties.AccessKey - which the .NET 10 WPF runtime does not yet surface through its text box peer, so screen readers hear the key on buttons and check boxes today. Verified through the UI Automation tree: keys unique per page, Alt+H focuses the LDAP host box, Alt+E invokes the dashboard's External setup button. Keys are unique per page, per dialog tab (with the dialog's Save global to every tab) and are enforced by build/check-mnemonics.py in the style workflow. MnemonicText (Services, tested) is the one parser and escape: data-driven captions are escaped with a doubled underscore. build/check-mnemonics.py also checks each translated catalogue (Resources/Strings..resx): every translated caption carries its own key, chosen for that language, and no two in a scope share one. Page titles are level-1 headings (AutomationProperties.HeadingLevel via the PageTitle style); the XAML views' status, empty-state and sign-in-error text blocks are live regions (source: Views/Mnemonic.cs; Services/MnemonicText.cs; build/check-mnemonics.py; App.xaml PageTitle style; Roadmap.md row "No screen-reader or keyboard audit has ever been done")

4.1 How the palette ranks what it found

Every source competes in one ranked list; a per-source offset is added to the text score, and lower wins. The offsets encode an editorial judgement about what a typed word most likely means (source: PaletteSearch.cs:118-130).

Source Score offset Rows offered The judgement, as the code states it
Page title +0 all matches the name of a page beats a phrase about it
Outcome phrase (IntentIndex) +2 6 a phrase beats an old name for the page
Page alias +4 all matches an old name beats "this page's group is called that"
Legacy navigation path +6 all matches a documented route must still land somewhere sensible
Group title +20 all matches
Individual setting +30 12 a query matching thirty settings and one page title almost always means the page
Page purpose +40 all matches last resort

The text score those offsets are added to is a ladder, and the rung a query lands on is decided in this order - the first one that answers yes wins (source: SearchTerms.cs, SearchQuery.Score and ScoreLoose):

flowchart TD
    Q["The typed query, normalised once:<br/>lower case, every run of punctuation to a space,<br/>stop words dropped, synonyms folded<br/>(junk to spam, ssl to tls, stuck to queue)"] --> EMPTY{"Anything left<br/>to search for?"}
    EMPTY -- no --> NM["No match"]
    EMPTY -- yes --> EX{"The whole candidate<br/>IS the query?"}
    EX -- yes --> S0["0 - exact"]
    EX -- no --> PFX{"The candidate starts<br/>with the query?"}
    PFX -- yes --> S5["5 - prefix"]
    PFX -- no --> ALL{"Every word of the query<br/>appears in the candidate?"}
    ALL -- yes --> S10["10 plus one per extra word the candidate has,<br/>capped at 4 - so that 'spam' puts<br/>Anti-spam settings above<br/>Bypass on SPF success"]
    ALL -- no --> SUB{"The query appears inside<br/>the candidate as typed?"}
    SUB -- yes --> S15["15 - substring"]
    SUB -- no --> MOST{"At least two words matched,<br/>and at least two thirds of them?"}
    MOST -- yes --> S25["25 plus the number that did not match"]
    MOST -- no --> VERB{"Two-word query, one word matched,<br/>and the one that did not is an action verb<br/>- add, stop, reset, schedule?"}
    VERB -- yes --> S27["27 - which is why 'reduce spam' finds<br/>the spam pages while 'spam ports'<br/>still finds nothing"]
    VERB -- no --> LOOSE{"A page or group title, and the<br/>query's letters appear in it in order?"}
    LOOSE -- yes --> S40["40 - subsequence, 'dq' to Delivery queue.<br/>Offered for names only: a subsequence over<br/>389 setting labels matches nearly all of them"]
    LOOSE -- no --> NM
Loading

Two words count as the same word when they are equal, when the query word is a prefix of the candidate's and at least three characters long ("log" reaches "logging", but "ip" deliberately does not reach "important"), or when the query word is the candidate's with up to four characters of inflection on it ("queued" reaches "queue"). Then the source offset from the table above is added; rows are sorted by score, then by how often this administrator has opened that page, then by title; tasks are cut to six and settings to twelve; and the three sections are ordered by their best row rather than by a fixed order, so an exact page name is never buried under six task phrases (source: PaletteSearch.Matches, Sort).

With nothing typed the palette shows Recently visited and Most used, five rows each, then every page in navigation order - because it is opened far more often to return somewhere than to discover something. The page you are already looking at is excluded from both shortcut lists. A history entry naming a page that no longer exists is skipped silently rather than shown as a dead row. Section captions are rows in the same list, so the list a test reasons about and the list the user sees are the same list; the arrow keys skip them and never wrap, because wrapping in a search palette loses the user's place.

4.2 Every outcome phrase the palette understands

97 phrases across 42 pages. They are matched word-wise with synonym folding (Services/SearchTerms.cs), so each one also covers its obvious rewordings - "mail is stuck" reaches "why is mail queued". PaletteSearchTests holds every entry to three things: the page must exist, the phrase must be unique, and typing the phrase must actually land on that page (source: IntentIndex.cs).

Page Say any of these
Server status (status) "is anyone connected"
Delivery queue (queue) "why is mail queued"; "mail is stuck"; "mail is not being delivered"; "outgoing mail is not sending"
Diagnosing stalled mail (stalledmail) "mail is stalled"; "sender times out after sending a message"; "accepted but never delivered"; "diagnose slow mail"
Live logs (logs) "messages are not arriving"; "find out why a message bounced"; "see what the server is doing right now"
Diagnostics (diagnostics) "test whether port 25 is open"; "check if the server is healthy"
MX query (mxquery) "where does mail for a domain go"
Logging (logging) "logs are filling the disk"; "turn on more detailed logging"; "send my logs to a log analyser"; "write the log to a database"; "make awstats work"
API & monitoring (api) "monitor the server from outside"
Domains (domains) "add a user"; "add a mailbox"; "create an e-mail address"; "add a domain"; "reset a user's password"; "change a user password"; "change a mailbox password"; "delete a user"; "remove a mailbox"; "disable an account"; "remove a domain"; "remove an alias"; "delete a mailing list"; "set up a mailing list"; "forward one address to another"; "set a mailbox size limit"; "collect mail from another provider"; "sign outgoing mail with dkim"
Public folders (publicfolders) "share a folder between accounts"
Delivery of e-mail (delivery) "send all mail through my provider"; "retry failed deliveries sooner"
Routes (routes) "send mail for one domain to a different server"
Incoming relays (relays) "my spam filter blames my own gateway"
Rules (rules) "act on messages as they arrive"
Server messages (servermessages) "change the bounce message wording"
Server sendout (sendout) "tell everyone about maintenance"
Spam filtering overview (spamoverview) "what is my spam configuration"; "which spam checks are running"; "why is my spam filter not catching anything"
Anti-spam settings (antispam) "stop spam"; "too much spam is getting through"; "turn on spamassassin"; "check spf dkim and dmarc"
SURBL servers (surbl) "block spam links in message bodies"
DNS blacklists (dnsbl) "block mail from a bad reputation ip"
White list (spamwhitelist) "legitimate mail is being marked as spam"; "let a sender through the spam filter"
Greylisting white list (greylistwhitelist) "mail from a new sender is delayed"
Anti-virus settings (antivirus) "scan for viruses"
Blocked attachments (blockedattachments) "block executable attachments"
Protocols (protocols) "searching a mailbox does not work"; "disconnect idle clients sooner"
TCP/IP ports (ports) "open the submission port"; "make clients use an encrypted connection"
Web services & autoconfiguration (webservices) "set up outlook automatically"; "configure a phone or tablet"
DNS resolver (dns) "use my own name servers"
SSL certificates (certs) "install a certificate"; "my certificate has expired"
Certificates (ACME) (acme) "renew certificates automatically"
SSL/TLS (tls) "which tls versions are allowed"; "limit how long a tls session can be resumed"
Transport security (security) "prove to other servers that tls is required"; "stop forwarded mail failing spf"; "reject forged bounce messages"
Authentication (authentication) "use microsoft 365 or google sign-in"; "make stored passwords harder to crack"; "do not offer authentication on port 25"
Administrative access (adminaccess) "change the administrator password"
Auto-ban (autoban) "someone is trying to guess passwords"; "lock out repeated failed logons"; "unban an address that was locked out"
IP ranges (ipranges) "block an ip"; "let a device send mail"; "let the office printer send e-mail"; "require authentication to send mail"; "make sure i am not an open relay"
Backup & restore (backup) "back up the server"; "restore from a backup"; "move to a new server"
Performance (performance) "the server is slow"
Advanced (advanced) "keep a copy of every message"
Event scripts (scripts) "run code when mail arrives"
Server limits & expert settings (hardening) "change a setting with no page"; "limit how many messages an account can send per minute"
About (about) "what version am i running"

4.3 Every legacy path that still resolves

54 paths, from the classic Administrator tree and from the 6.2.18 Control Panel this reorganisation replaced. Typing one into the palette lands on the page that owns the subject today. Without this table, reorganising the navigation would quietly invalidate every set of instructions ever written about this product (source: NavigationMap.LegacyPaths).

The path an old note, forum answer or manual gives Opens
Status > Server status Server status (status)
Status > Delivery queue Delivery queue (queue)
Status > Live logs Live logs (logs)
Utilities > Diagnostics Diagnostics (diagnostics)
Utilities > MX-query MX query (mxquery)
Settings > Logging Logging (logging)
Settings > Network > API & monitoring API & monitoring (api)
Settings > Public folders Public folders (publicfolders)
Settings > Advanced > Groups
Settings > Maintenance > Groups
Groups (groups)
Settings > Delivery of e-mail Delivery of e-mail (delivery)
Settings > Advanced > Routes Routes (routes)
Settings > Advanced > Incoming relays
Settings > Network > Incoming relays
Incoming relays (relays)
Settings > Advanced > Server messages
Settings > Maintenance > Server messages
Server messages (servermessages)
Utilities > Server sendout Server sendout (sendout)
Settings > Anti-spam Anti-spam settings (antispam)
Settings > Anti-spam > SURBL servers SURBL servers (surbl)
Settings > Anti-spam > DNS blacklists DNS blacklists (dnsbl)
Settings > Anti-spam > White list White list (spamwhitelist)
Settings > Anti-spam > Greylisting white list Greylisting white list (greylistwhitelist)
Settings > Anti-virus Anti-virus settings (antivirus)
Settings > Anti-virus > Blocked attachments Blocked attachments (blockedattachments)
Settings > Protocols
Settings > Protocols > SMTP
Settings > Protocols > POP3
Settings > Protocols > IMAP
Protocols (protocols)
Settings > Advanced > TCP/IP ports
Settings > Network > TCP/IP ports
TCP/IP ports (ports)
Settings > Network > Web services & autoconfiguration Web services & autoconfiguration (webservices)
Settings > Network > DNS resolver DNS resolver (dns)
Settings > Advanced > SSL certificates
Settings > Security > SSL certificates
SSL certificates (certs)
Settings > Security > Certificates (ACME) Certificates (ACME) (acme)
Settings > Security > SSL/TLS
Settings > Advanced > SSL/TLS
SSL/TLS (tls)
Settings > Security > Transport security Transport security (security)
Settings > Security > Authentication Authentication (authentication)
Settings > Security > Administrative access Administrative access (adminaccess)
Settings > Security > Auto-ban & SSL/TLS
Settings > Security > Auto-ban
Settings > Advanced > Auto-ban
Auto-ban (autoban)
Settings > Advanced > IP ranges
Settings > Security > IP ranges
IP ranges (ipranges)
Utilities > Backup
Utilities > Backup & restore
Backup & restore (backup)
Settings > Maintenance > Performance Performance (performance)
Settings > Advanced
Settings > Maintenance > Advanced & scripting
Advanced (advanced)
Settings > Advanced > Scripts
Settings > Maintenance > Event scripts
Event scripts (scripts)
Settings > Advanced hardening
Settings > Maintenance > Advanced INI settings
Server limits & expert settings (hardening)

Settings > Security > Auto-ban & SSL/TLS is the one path whose page was split in two. It resolves to the brute-force half, because somebody typing it is following a note about repeated logon failures far more often than one about cipher suites, and the auto-ban settings are the ones with no other home. The old title stays an alias on both pages, so typing it offers the two of them, and each page signposts the other beside the breadcrumb.

5. The navigation map (every page, key, title, purpose)

Registered page factories: 55 keys (source: MainWindow.xaml.cs RegisterPages() :114-171). The tree (source: NavigationMap.cs BuildRoots() :344-757):

Every key registered in MainWindow appears in the map exactly once, and NavigationMapTests fails if the two sets differ - a page that stops being reachable is a regression, not a simplification. A duplicated key throws at start-up rather than giving one page two homes.

Key Title Group Factory in MainWindow.RegisterPages Related links Aliases the palette also matches
welcome Welcome (top level) WelcomeView() dashboard Home, Start, Getting started, Quick actions, First steps
dashboard Dashboard (top level) DashboardView() status, queue Overview, Charts, Graphs, Statistics, Counters, Throughput
status Server status Monitoring & troubleshooting StatusView() queue, dashboard, logs Sessions, Connections, Uptime, Running, Service state, Live sessions
queue Delivery queue Monitoring & troubleshooting QueueView() logs, delivery, status, stalledmail Spool, Stuck mail, Pending mail, Outbound queue, Retry, Not delivered, Backlog
stalledmail Diagnosing stalled mail Monitoring & troubleshooting StalledMailView() queue, logs, diagnostics Stalled mail, Slow mail, Mail is stalled, Timed out while sending end of data, Accepted but never delivered, Hung, Wedged, Stall diagnosis, Diagnosing slow mail
messagetrace Message trace Monitoring & troubleshooting MessageTraceView() queue, logs, quarantine Message trace, Delivery history, Where did my email go, Did it arrive, Track a message, Was it delivered, Message log, Audit, What happened to, Trace
logs Live logs Monitoring & troubleshooting LogsView() logging, queue, diagnostics Log viewer, Tail, Trace, Debug, SMTP conversation, Error log, Awstats
diagnostics Diagnostics Monitoring & troubleshooting DiagnosticsView() mxquery, logs, queue Self test, Health check, Port 25 test, Connectivity, Message store consistency, Missing files
mxquery MX query Monitoring & troubleshooting MxQueryView() routes, dns, diagnostics MX-query, MX lookup, DNS lookup, nslookup, Mail exchanger, Where does mail go
externalsetup External setup Monitoring & troubleshooting ExternalSetupView() security, diagnostics, domains External prerequisites, Prerequisites, Setup checklist, Checklist, Outside the server, What still needs doing, Publish a DNS record, TXT record, Inert features, Action needed
dnsrecords DNS records Monitoring & troubleshooting DnsRecordsView() externalsetup, domains, security SPF record, DKIM record, DMARC record, MTA-STS record, TLS-RPT record, TXT records, Reverse DNS, PTR record, What DNS records do I need, Publish DNS records, DNS checklist, Sender authentication
logging Logging Monitoring & troubleshooting ServerSettingsView(ServerSettingsView.Section.Logging) logs, performance Log level, Log files, Log folder, Retention, Delete old logs, Verbosity, Debug logging
api API & monitoring Monitoring & troubleshooting FeatureSettingsView(FeatureSettingsView.Section.Integration) apikeys, logging, status, webservices REST, Prometheus, Grafana, Metrics, Health endpoint, Nagios, Zabbix, Webhooks, Automation
apikeys REST API keys Monitoring & troubleshooting ApiKeysView() api, adminaccess API key, Bearer token, Access token, Credential, Service account for the API, Revoke a key, hmapi, Automation credential, Read-only key
domains Domains Accounts & domains DomainsView() publicfolders, groups, rules Accounts, Users, Mailboxes, Add a user, Aliases, Distribution lists, Mailing list, Quota, Size limit, DKIM, External accounts, Domain aliases, Signatures, Forwarding
publicfolders Public folders Accounts & domains PublicFoldersView() groups, domains Shared folders, Shared mailbox, IMAP ACL, Permissions, Team folder, Shared calendar
groups Groups Accounts & domains CollectionSpecs.Groups() publicfolders, domains Security groups, Roles, Membership, Permission groups
delivery Delivery of e-mail Mail flow & delivery ServerSettingsView(ServerSettingsView.Section.Delivery) routes, queue, ipranges SMTP relayer, Smarthost, Send through my ISP, Retries, Bounce, Non-delivery report, Host name, Max message size, Deliver local to local
routes Routes Mail flow & delivery RoutesView() delivery, mxquery Static route, Transport map, Send domain to server, Internal relay, Hybrid, Split delivery
relays Incoming relays Mail flow & delivery IncomingRelaysView() ipranges, antispam Trusted relay, Upstream gateway, Front-end filter, Load balancer, Proxy, Received header
rules Rules Mail flow & delivery RulesView() domains, scripts Global rules, Filters, Conditions and actions, Sieve, Auto-reply, Move to folder, Tag subject
servermessages Server messages Mail flow & delivery CollectionSpecs.ServerMessages() delivery, protocols Banner, Greeting, Bounce text, Error text, Templates, Wording, Localise messages
sendout Server sendout Mail flow & delivery SendoutView() domains Announcement, Mail all users, Broadcast, Notify everyone, Maintenance notice
spamoverview Spam filtering overview Spam & virus filtering SpamOverviewView() antispam, spamwhitelist, logs Spam overview, Anti-spam overview, Spam pipeline, Spam summary, What is my spam configuration, Spam checks, Order of spam checks, Spam thresholds, Spam verdict, Why was this marked as spam
quarantine Quarantine Spam & virus filtering QuarantineView() antispam, spamoverview, spamwhitelist Quarantine, Held messages, Release a message, False positive, Review spam, Quarantined mail, Where did my email go, Blocked message, Recover a message, Spam review queue
antispam Anti-spam settings Spam & virus filtering ServerSettingsView(ServerSettingsView.Section.AntiSpam) spamoverview, dnsbl, surbl, spamwhitelist, greylistwhitelist Spam filter, Stop spam, Score, Threshold, SPF, DKIM, DMARC, ARC, Greylisting, SpamAssassin, Junk mail, PTR, HELO check, Subject prefix
surbl SURBL servers Spam & virus filtering CollectionSpecs.SurblServers() spamoverview, antispam, dnsbl SURBL, URI block list, Link blacklist, Body URL check, Spamhaus DBL
dnsbl DNS blacklists Spam & virus filtering CollectionSpecs.DnsBlackLists() spamoverview, antispam, surbl, ipranges DNSBL, DNS blacklists (DNSBL), RBL, Blackhole list, Spamhaus, Barracuda, Block an IP by reputation
spamwhitelist White list Spam & virus filtering CollectionSpecs.SpamWhiteList() spamoverview, antispam, greylistwhitelist Anti-spam white list, Allow list, Safe senders, False positive, Let a sender through, Exempt from spam filter
blockedsenders Blocked senders Spam & virus filtering CollectionSpecs.BlockedSenders() spamwhitelist, antispam, spamoverview Blacklist, Block list, Deny list, Block a sender, Banned senders, Refuse mail from
greylistwhitelist Greylisting white list Spam & virus filtering CollectionSpecs.GreyListWhiteList() spamoverview, antispam, spamwhitelist Greylist exemption, Greylisting delay, Slow mail, First message delayed, Retry delay
virusoverview Virus scanning overview Spam & virus filtering VirusOverviewView() antivirus, blockedattachments, logs Anti-virus overview, Antivirus overview, Virus overview, Virus summary, What is my virus configuration, Is virus scanning working, Am I scanning for viruses, Scanner not working, ClamAV not scanning, Unscanned mail
antivirus Anti-virus settings Spam & virus filtering ServerSettingsView(ServerSettingsView.Section.AntiVirus) virusoverview, blockedattachments Virus scanner, ClamAV, Malware, Infected mail, Scan attachments, External scanner
blockedattachments Blocked attachments Spam & virus filtering CollectionSpecs.BlockedAttachments() virusoverview, antivirus Attachment blocking, Block exe, File extensions, Strip attachments, Dangerous files, Zip
protocols Protocols Connections & protocols ServerSettingsView(ServerSettingsView.Section.Protocols) ports, delivery, authentication SMTP, POP3, IMAP, Timeouts, Max connections, Message size limit, Authentication required, Welcome banner
ports TCP/IP ports Connections & protocols TcpIpPortsView() certs, tls, protocols Listeners, Bindings, Port 25, Port 465, Port 587, Submission, Port 993, Port 995, STARTTLS, Implicit TLS, Interface, Bind address, Open a port
webservices Web services & autoconfiguration Connections & protocols FeatureSettingsView(FeatureSettingsView.Section.WebServices) acme, security, api HTTP, Autodiscover, Autoconfig, Outlook setup, Thunderbird setup, Mobile setup, MTA-STS policy, Well-known, Web server
dns DNS resolver Connections & protocols FeatureSettingsView(FeatureSettingsView.Section.Dns) security, antispam, mxquery Name servers, Resolver, DNSSEC, Lookup failures, DNS cache, EDNS
certs SSL certificates TLS & certificates SslCertificatesView() acme, ports, security Certificate, SSL certificate, TLS certificate, PFX, PEM, Expiry, Renew, Install a certificate, Chain, Private key
acme Certificates (ACME) TLS & certificates FeatureSettingsView(FeatureSettingsView.Section.Automation) certs, webservices, ports ACME, Let's Encrypt, Letsencrypt, Automatic renewal, http-01, Certbot, Free certificate
tlsoverview Transport encryption overview TLS & certificates TlsOverviewView() tls, certs, ports TLS overview, SSL overview, Encryption overview, Is my mail encrypted, Plaintext password, Passwords in the clear, Unencrypted port, Certificate expiry, Certificate expiring, What TLS am I using, STARTTLS not required
tls SSL/TLS TLS & certificates ServerSettingsView(ServerSettingsView.Section.Tls) tlsoverview, certs, ports, security, autoban Auto-ban & SSL/TLS, TLS versions, TLS 1.0, TLS 1.1, TLS 1.2, TLS 1.3, Ciphers, Cipher list, Cipher suites, OpenSSL cipher string, Disable old TLS, Weak ciphers, ChaCha20, Verify remote certificate
security Transport security TLS & certificates FeatureSettingsView(FeatureSettingsView.Section.Security) certs, dns, webservices DANE, TLSA, MTA-STS, TLS-RPT, TLS reporting, ARC, Downgrade, Opportunistic TLS, Enforce TLS
authentication Authentication Access & abuse protection FeatureSettingsView(FeatureSettingsView.Section.Authentication) adminaccess, autoban, ipranges, protocols Password hashing, bcrypt, Argon2, OAuth2, XOAUTH2, Modern authentication, Microsoft 365, Google, SASL, CRAM-MD5, APOP, Two-factor, Pepper
ldap Directory authentication (LDAP) Access & abuse protection LdapSettingsView() authentication, adminaccess, domains, directorysync LDAP, Active Directory, AD, Directory, Domain password, Bind, LDAPS, StartTLS, Single sign-on, Domain accounts, DC
directorysync Directory synchronisation Access & abuse protection DirectorySyncView() ldap, domains Directory sync, Provisioning, Provision accounts, Account source, Bulk create accounts, Import users, Sync users, LDAP sync, AD sync, Create accounts from Active Directory, Onboarding, Leavers
adminaccess Administrative access Access & abuse protection ServerSettingsView(ServerSettingsView.Section.AdminAccess) authentication Admin password, Administrator password, Change the admin password, Remote administration, COM API access
autoban Auto-ban Access & abuse protection ServerSettingsView(ServerSettingsView.Section.AutoBan) ipranges, authentication, tls, logs Auto-ban & SSL/TLS, Autoban, Brute force, Bruteforce, Lockout, Locked out, Failed logons, Failed logins, Password guessing, Dictionary attack, Fail2ban, Hammering, Temporary ban, Logon failure list
ipranges IP ranges Access & abuse protection IPRangesView() authentication, autoban, relays, delivery IP range, Firewall, Block an IP, Allow an IP, Open relay, Relay permissions, Require authentication, Let a device send mail, Printer, Scanner, LAN, Localhost, My IP
backup Backup & restore Maintenance BackupView() hardening, domains Backup, Restore, Export, Import, Disaster recovery, Copy to another server, Migrate
performance Performance Maintenance ServerSettingsView(ServerSettingsView.Section.Performance) hardening, logging Threads, Workers, Database connections, Cache, Slow server, Tuning, Concurrency, Memory
advanced Advanced Maintenance ServerSettingsView(ServerSettingsView.Section.Advanced) scripts, hardening Advanced & scripting, Archive, Archiving, Mirror, Default domain, Scripting engine, Enable scripting, VBScript, JScript, IPv6 preference
scripts Event scripts Maintenance ScriptsView() advanced, rules EventHandlers, Scripts, OnDeliverMessage, OnClientLogon, OnAcceptMessage, VBScript, Run code on mail
hardening Server limits & expert settings Maintenance FeatureSettingsView(FeatureSettingsView.Section.Hardening) advanced, performance, backup Advanced INI settings, Advanced hardening, INI settings, hMailServer.INI, Registry, Undocumented, Expert settings, Everything else, Timeouts, Queue bounds, Sending limits, Rate limit, Throttle, fsync, Durability, DPAPI, Received headers, mailer-daemon
about About (top level) AboutView() Version, Build, Licence, License, Support, Credits, Copyright

The eight group icons, in tree order, are Pulse24, Globe24, MailArrowForward20, ShieldCheckmark24, PlugConnected24, LockClosed24, ShieldKeyhole24 and Wrench24; they are named as strings so NavigationMap stays WPF-free, and an unknown name renders no icon rather than failing - loudly in DEBUG builds, because Enum.TryParse failing silently is how a mistyped name once shipped one group bare among seven with glyphs.

The bullets below add what the table cannot: the on-screen title where it differs from the navigation label, and the reasoning behind a page's placement.

Top level:

  • welcome "Welcome" - Start here: jump straight to a common task, or press Ctrl+K (class WelcomeView)
  • dashboard "Dashboard" - Live counters and charts for processed mail, spam, viruses and sessions (DashboardView)

Group "Monitoring & troubleshooting" (icon Pulse24):

  • status "Server status" (StatusView)
  • queue "Delivery queue" (QueueView)
  • stalledmail "Diagnosing stalled mail" (Views/StalledMailView.cs; added 5 Sep 2026, v6.2.25) - which half stalled (accepting vs delivering), "Turn on debug logging now" / "Turn it off again" (Settings.Logging.Enabled + LogDebug over COM), the what-the-log-shows table, the saturation report, the nine bounding settings with buttons to their pages, "Open the full guide"; aliases "Stalled mail", "Mail is stalled", "Accepted but never delivered", "Diagnosing slow mail"; related: queue, logs, diagnostics
  • messagetrace "Message trace" (MessageTraceView)
  • logs "Live logs" (LogsView)
  • diagnostics "Diagnostics" (DiagnosticsView in UtilityViews.cs)
  • mxquery "MX query" (MxQueryView in UtilityViews.cs)
  • externalsetup "External setup" (ExternalSetupView)
  • dnsrecords "DNS records" (DnsRecordsView)
  • logging "Logging" (ServerSettingsView Section.Logging)
  • api "API & monitoring" (FeatureSettingsView Section.Integration)
  • apikeys "REST API keys" (ApiKeysView)

Group "Accounts & domains" (Globe24):

  • domains "Domains" (DomainsView; on-screen title "Domains & accounts" - DomainsView.xaml:16)
  • publicfolders "Public folders" (PublicFoldersView)
  • groups "Groups" (CollectionSpecs.Groups -> GroupsPageView)

Group "Mail flow & delivery" (MailArrowForward20):

  • delivery "Delivery of e-mail" (ServerSettingsView Section.Delivery)
  • routes "Routes" (RoutesView)
  • relays "Incoming relays" (IncomingRelaysView in UtilityViews.cs)
  • rules "Rules" (RulesView; on-screen title "Global rules" - RulesView.xaml:18)
  • servermessages "Server messages" (CollectionSpecs.ServerMessages)
  • sendout "Server sendout" (SendoutView in UtilityViews.cs)

Group "Spam & virus filtering" (ShieldCheckmark24):

  • spamoverview "Spam filtering overview" (SpamOverviewView, read-only)
  • quarantine "Quarantine" (QuarantineView)
  • antispam "Anti-spam settings" (ServerSettingsView Section.AntiSpam; on-screen title "Anti-spam")
  • surbl "SURBL servers" (CollectionSpecs.SurblServers)
  • dnsbl "DNS blacklists" (CollectionSpecs.DnsBlackLists; on-screen "DNS blacklists (DNSBL)")
  • spamwhitelist "White list" (CollectionSpecs.SpamWhiteList; on-screen "Anti-spam white list")
  • blockedsenders "Blocked senders" (CollectionSpecs.BlockedSenders)
  • greylistwhitelist "Greylisting white list" (CollectionSpecs.GreyListWhiteList)
  • virusoverview "Virus scanning overview" (VirusOverviewView, read-only)
  • antivirus "Anti-virus settings" (ServerSettingsView Section.AntiVirus; on-screen "Anti-virus")
  • blockedattachments "Blocked attachments" (CollectionSpecs.BlockedAttachments)

Group "Connections & protocols" (PlugConnected24):

  • protocols "Protocols" (ServerSettingsView Section.Protocols)
  • ports "TCP/IP ports" (TcpIpPortsView)
  • webservices "Web services & autoconfiguration" (FeatureSettingsView Section.WebServices; on-screen "Web services & client autoconfiguration")
  • dns "DNS resolver" (FeatureSettingsView Section.Dns)

Group "TLS & certificates" (LockClosed24):

  • certs "SSL certificates" (SslCertificatesView)
  • acme "Certificates (ACME)" (FeatureSettingsView Section.Automation; on-screen "Automatic certificates (ACME)")
  • tlsoverview "Transport encryption overview" (TlsOverviewView, read-only)
  • tls "SSL/TLS" (ServerSettingsView Section.Tls)
  • security "Transport security" (FeatureSettingsView Section.Security)

Group "Access & abuse protection" (ShieldKeyhole24):

  • authentication "Authentication" (FeatureSettingsView Section.Authentication)
  • ldap "Directory authentication (LDAP)" (LdapSettingsView)
  • directorysync "Directory synchronisation" (DirectorySyncView)
  • adminaccess "Administrative access" (ServerSettingsView Section.AdminAccess)
  • autoban "Auto-ban" (ServerSettingsView Section.AutoBan)
  • ipranges "IP ranges" (IPRangesView)

Group "Maintenance" (Wrench24):

  • backup "Backup & restore" (BackupView)
  • performance "Performance" (ServerSettingsView Section.Performance)
  • advanced "Advanced" (ServerSettingsView Section.Advanced; on-screen "Advanced"; was "Advanced & scripting" until 5 September 2026 - the old title stays as a search alias, and the Scripting card is now titled "Scripting engine" with the Event scripts page named as where the script is edited)
  • scripts "Event scripts" (ScriptsView)
  • hardening "Server limits & expert settings" (FeatureSettingsView Section.Hardening; formerly "Advanced INI settings"/"Advanced hardening", key unchanged)

Top level:

  • about "About" (AboutView)

Design rules pinned by tests: every registered key appears in the map exactly once (NavigationMapTests); page titles do not change - better names are aliases; "Auto-ban & SSL/TLS" is the one page that was split, and both halves carry the old title as an alias and signpost each other (PageSplitTests) (source: NavigationMap.cs class summary and comments on tls/autoban)

Legacy paths the palette resolves (old Administrator and 6.2.18 Control Panel paths) include "Status > Server status", "Settings > Protocols", "Settings > Anti-spam", "Settings > Advanced > Routes", "Settings > Security > Auto-ban & SSL/TLS" -> autoban, "Settings > Security > SSL/TLS" -> tls, "Settings > Maintenance > Advanced INI settings" -> hardening, "Utilities > Backup & restore" -> backup, and others (source: NavigationMap.cs LegacyPaths dictionary)

6. How settings are read and written

  • Two data-driven settings views: ServerSettingsView edits the COM app.Settings tree via dotted paths ("AntiSpam.SpamMarkThreshold") resolved by reflection over IDispatch; FeatureSettingsView edits hMailServer.INI [Settings] keys through IniFeatureStore (source: Views/ServerSettingsView.xaml.cs:16-21, ResolveOwner/GetProperty/SetProperty; Views/FeatureSettingsView.xaml.cs:27-33)
  • ServerSettingsView pages also carry INI-backed rows (IniBool/IniNumber/IniText/SectionIniNumber) so that a setting sits with its feature regardless of where it is stored (source: ServerSettingsView.xaml.cs BuildProtocols comments; Save_Click IIniSetting)
  • ServerSettingsView "Save" writes every row on every tab of the page; toast "Saved N settings — applied immediately." or "... — INI settings need a service restart."; rows marked inert (INotPersisted) and action buttons are not counted; the Advanced page additionally calls Settings.Scripting.Reload() (source: ServerSettingsView.xaml.cs:3325-3396)
  • FeatureSettingsView "Save" writes every card, then asks "Settings saved. The hMailServer service must be restarted for the changes to take effect. Restart it now?" and, on Yes, restarts the Windows service hMailServer via ServiceController (60 s waits) (source: FeatureSettingsView.xaml.cs:2948-3040)
  • IniFeatureStore locates the INI from HKLM\SOFTWARE\hMailServer\InstallLocation (Bin\hMailServer.INI, then hMailServer.INI) in the 64- then 32-bit registry view, else from the hMailServer service's PathName via WMI (hMailServer.ini beside the exe); IsAvailable = file found locally (source: Services/IniFeatureStore.cs:61-120)
  • Read/Write of [Settings] uses the local file when available, otherwise COM GetIniSetting/SetIniSetting; a COM write also updates the database mirror; writes always SetIniSetting even for an empty value (source: IniFeatureStore.cs:130-187; ServerSession.cs SetCurrent comment)
  • [Database] and [Directories] (and any non-[Settings] section) are readable/writable only from the server machine - deliberately not exposed over COM (source: IniFeatureStore.cs:39-44, 199-212)
  • Secrets (OAuth2 HMAC, SRS, BATV, password pepper, metrics token/password, service account password) use a write-only SecretSetting editor that never shows the stored value and writes only when a new value is typed; several offer "generate" (source: settings.md:54-55; FeatureSettingsView.xaml.cs SecretSetting usages with OfferGenerate)
  • Every settings row has a stable AutomationId (its COM path or INI key) and an accessible name; blurbs are printed under the editor and attached as help text (source: ServerSettingsView.xaml.cs ComSetting.Describe/Annotate; FeatureSettingsView.xaml.cs Setting.Describe/Annotate)
  • SettingClaims pins wording for settings the server only partly honours: WorkerThreadPriority is INERT (shown read-only, "the server stores this value and never reads it"); UserInterfaceLanguage only reaches third-party COM tools; Cache.*MaxSizeKb apply immediately but reset to 10240 KB at every restart; JsonLogging is overridden while Logging.LogFormat is NCSA; Logging.LogFormat NCSA = NCSA Common Log Format (not combined, not AWStats); Logging.Device SQL falls back to files if the database is unreachable; OtelEndpoint = traces only, OtelMetricsEndpoint, OtelLogsEndpoint separate (source: Services/SettingClaims.cs:96-236)

6.1 Which store a value lives in, and what that costs

flowchart TD
    A["A row on a settings page"] --> B{"Where is the value kept?"}
    B -->|"the COM settings tree"| C["ServerSettingsView ComBool, ComText, ComCombo<br/>dotted path resolved by reflection over IDispatch<br/>for example AntiSpam.SpamMarkThreshold"]
    B -->|"hMailServer.INI, Settings section"| D{"Which view owns the row?"}
    D -->|FeatureSettingsView| E["IniFeatureStore.Read and Write"]
    D -->|"ServerSettingsView IniBool, IniNumber, IniText"| F["IniFeatureStore, but guarded on IsAvailable"]
    B -->|"another INI section"| G["ProfileApi straight onto the local file<br/>LDAP, Database, Directories, SendingLimits"]
    C --> H["Saved at once, and live at once"]
    E --> I["Saved, then the page offers to restart the service"]
    F --> J["Saved, and a toast says INI settings need a restart"]
    G --> K["LDAP is re-read within 2 s. SendingLimits within seconds.<br/>Database and Directories need a restart."]
Loading

IniFeatureStore finds the file from HKLM\SOFTWARE\hMailServer\InstallLocation (Bin\hMailServer.INI, then hMailServer.INI) in the 64-bit then the 32-bit registry view, and failing that from the hMailServer service's PathName through WMI. When the file is on this machine it is preferred over the COM route even though both exist, because the file is the copy the server itself runs on - the database mirror follows it, not the other way round - so reading it cannot show a value the running server disagrees with (source: IniFeatureStore.cs:61-152).

[Database] and [Directories] are deliberately unreachable over COM. They are the bootstrap that says where the database is, and a server whose database location could be changed through the database it is currently using is a server that can be pointed somewhere else by anyone who reaches the one it is on (source: IniFeatureStore.cs:39-44).

6.2 What a session connected to another machine cannot do

ServerSession.IsLocalSession decides this, from LocalHostNames: an empty name, localhost, 127.0.0.1, ::1, [::1], this machine's own name, or a fully-qualified name whose first label is this machine's name. Nothing is resolved - a DNS lookup here would run on the UI thread, which is the problem the whole area is trying to get away from.

Page or feature Remote behaviour Source
Every FeatureSettingsView page - Transport security, Certificates (ACME), API & monitoring, Server limits & expert settings, Authentication, DNS resolver, Web services The cards are not built at all. Subtitle: "hMailServer.INI was not found on this machine. These settings can only be edited on the server itself." Save is disabled FeatureSettingsView.xaml.cs:2614-2618
Directory authentication (LDAP) Save disabled, with the same sentence plus "the connection test still works with the values typed here" LdapSettingsView.cs:686-688
Directory synchronisation The six [LDAP] selection boxes are read-only and show their defaults; Preview and Apply still run, inside the server DirectorySyncView.cs:420, 441-445
INI-backed rows on a ServerSettingsView page (timeouts, JsonLogging, the tarpits, QuotaWarningPercent, and the rest) The editor shows the value the server uses when the key is absent, not the configured one; SaveToIni returns without writing, and the row is still counted in "Saved N settings" ServerSettingsView.xaml.cs:342-344, 365-366, 3208-3212
Live logs "Log folder not found on this machine (live logs need a local server)." LogsView.xaml.cs
REST API keys The store is hMailServerApiKeys.ini beside the INI; the page needs the path ApiKeyStore.cs:134-137
TCP/IP ports "Listening" badge Every row reads Unknown - the TCP table probed is this machine's TcpIpPortsView.xaml.cs:64-67
SSL certificates: certificate and key inspection "cannot be checked from here" rather than a confident report about the wrong disk CertificateInspector.cs:102
Event scripts The script is loaded from and saved to local disk ScriptsView.cs
Diagnostics: message-store consistency card Says the same number is published as the metric hmailserver_messagestore_missing_files UtilityViews.cs ReadConsistencyReport
External setup: the Windows service account item Answers "Cannot tell" instead of guessing ExternalSetupChecks.cs:203
Backup: the schedule card and BackupMessagesDBOnly Disabled, with an explanation. The COM half of the page works BackupView.xaml.cs:31, 196

A COM route for [Settings] does exist and is wired up at connect time (IniFeatureStore.ComReadSetting/ComWriteSetting -> Settings.GetIniSetting/SetIniSetting), and IniFeatureStore.Read/Write use it when the file is absent. What no page does today is offer it: every INI-backed editor gates on IsAvailable - the file - rather than on SettingsReachable, which is defined and never called. So the honest summary is: INI settings are edited on the server, over Remote Desktop or the REST API.

6.3 When a change takes effect

Store Applies The page says
COM settings tree at once "Saved N settings — applied immediately."
hMailServer.INI [Settings], from a FeatureSettingsView page after a service restart "Settings saved. The hMailServer service must be restarted for the changes to take effect. Restart it now?" — and on Yes it does it
hMailServer.INI [Settings], from a ServerSettingsView row after a service restart "Saved N settings — INI settings need a service restart." No restart is offered
hMailServer.INI [LDAP] within two seconds no restart mentioned; the server re-reads on the file's timestamp
hMailServer.INI [SendingLimits] / [SendingLimitsOverrides] within seconds stated on the card
hMailServerApiKeys.ini on the next request the page says no restart is needed
COM Settings.Backup, from the Backup page's Save settings button at once IInterfaceBackupSettings has no Save method of its own: each property setter writes straight through, so the page's Save is only what triggers the assignments
The event script on "Save & reload" Scripting.Reload() then CheckSyntax()
Settings.Scripting.* on the Advanced page on Save, which also calls Scripting.Reload()

The restart the Control Panel offers is done by ServiceController when it is already elevated, and otherwise by handing net stop hMailServer & net start hMailServer to cmd.exe with Verb = runas - one UAC prompt instead of an opaque "Cannot open hMailServer service". It waits 60 seconds for each transition, then reconnects the COM session with 20 attempts 500 ms apart, because the service registers its class factory a moment before it can serve calls. A cancelled UAC prompt is reported as "the elevation prompt was cancelled." (source: FeatureSettingsView.xaml.cs Save_Click :2778, RestartService(), TryRestartService(), Reattach()).

6.4 The tab and card map of the two data-driven views

ServerSettingsView - ten pages, tabbed, COM-backed with INI rows mixed in:

Page Tabs Cards
Protocols Services, SMTP, IMAP, POP3, Timeouts Services; SMTP; IMAP; IMAP search limits; Change history for synchronising clients; Repair; POP3; How often a client may check for mail; Idle timeouts
Delivery of e-mail Delivery, Relayer, Rules Delivery of e-mail; SMTP relayer (smart host); OAuth2 for the relay (Microsoft 365); Rules
Anti-spam General, Sender auth, Host checks, Greylisting, SpamAssassin Thresholds & actions; Recipient tarpit; Quarantine instead of refusing; Sender authentication; Connecting host checks; Greylisting; SpamAssassin; External filtering engine (rspamd and anything else that speaks HTTP)
Anti-virus General, ClamAV, ClamWin, Custom Action & notifications; When a scanner cannot run; ClamAV (network daemon); ClamWin (local executable); Custom scanner
SSL/TLS Protocol versions, Ciphers, Session resumption Protocol versions; Ciphers & verification; Session resumption
Auto-ban Auto-ban Auto-ban; Per-name lockout; Logon tarpit
Logging Logging Log categories; Log detail; Log retention; Message trace; Log files
Performance Threads, Cache, Indexing, Database Threads; Cache; How the caches are performing; Message indexing; Database connections
Advanced General, Copies of mail, Scripting General; Mirroring; Message archiving; Disk space; Scripting engine
Administrative access Password, Two-factor Administrator password; Policy for mailbox passwords; Reuse and expiry; Second factor on the administrator credential (recommended); Two-factor for this Control Panel only

FeatureSettingsView - seven pages, card-per-subject, hMailServer.INI [Settings]:

Page Section Cards
Transport security Security DANE & DNSSEC; MTA-STS; ARC sealing; ARC inbound filtering; DKIM signature timestamps; DKIM oversigning; Authentication results on inbound mail; TLS reporting (TLS-RPT); DMARC aggregate reporting (rua); Forwarded mail & bounce protection (SRS / BATV)
Certificates (ACME) Automation ACME (Let's Encrypt)
API & monitoring Integration REST administration API + Web Control Deck; Monitoring; Updates; Windows Event Log; ManageSieve (RFC 5804); Operability
Server limits & expert settings Hardening Timeouts and queue bounds; Front-end proxies (PROXY protocol and XCLIENT); Received headers; Refused connections; Message store durability; Per-account sending limits; Submission rate limits; Server-generated mail; Low-level tuning; Stored secret protection; Windows service account; Settings that used to be on this page
Authentication Authentication OAuth2 / external identity provider; Password storage; SMTP authentication
DNS resolver Dns Name servers; DNS cache; DNS blacklist checks
Web services & autoconfiguration WebServices Listener; Client autoconfiguration (autoconfig & autodiscover); MTA-STS policy hosting; Calendar and contacts discovery (CalDAV / CardDAV)

6.5 How many settings each page owns

From the committed index, which is what the palette searches. The authoritative list of keys, defaults and effects is Settings Reference, generated from the server source; this table is only the shape of the Control Panel's own coverage (source: Services/SettingsSearchIndex.g.cs, counted per owning page).

Page Settings in the index View
Anti-spam settings 53 ServerSettingsView
Protocols 45 ServerSettingsView
API & monitoring 38 FeatureSettingsView
Server limits & expert settings 32 FeatureSettingsView
Performance 27 ServerSettingsView
Delivery of e-mail 27 ServerSettingsView
Authentication 22 FeatureSettingsView
Transport security 21 FeatureSettingsView
Anti-virus settings 19 ServerSettingsView
Logging 18 ServerSettingsView
SSL/TLS 14 ServerSettingsView
Backup & restore 14 hand-written (BackupView)
Web services & autoconfiguration 13 FeatureSettingsView
Advanced 12 ServerSettingsView
IP ranges 8 hand-written (IPRangesView)
Auto-ban 8 ServerSettingsView
Administrative access 8 ServerSettingsView
Certificates (ACME) 7 FeatureSettingsView
DNS resolver 3 FeatureSettingsView
Total 389

Every row in the index carries a stable AutomationId - its COM path or INI key - and an accessible name, and its blurb is printed under the editor and attached as help text, so the statement the page makes about a setting is the same statement a screen reader hears.

7. Page-by-page reference

7.0 Welcome and Dashboard

  • Welcome: title "Welcome", line "Connected to hMailServer {version} on {host}.", subtitle "Start with what you want to do, browse by area below, or press Ctrl+K to search every page and setting."; heading "What do you want to do?" with twelve intent rows (Services/WelcomeIntents.cs; added 5 September 2026, v6.2.25: "Mail is stuck or slow" -> stalledmail, "See what is waiting to go out" -> queue, "Watch the server work" -> logs, "Add a domain or a mailbox" -> domains, "Stop the spam" -> spamoverview, "Someone is guessing passwords" -> autoban, "Let a device or printer send mail" -> ipranges, "Renew or install a certificate" -> certs, "Send everything through my provider" -> delivery, "Check the server is healthy" -> diagnostics, "Back up the configuration and the mail" -> backup, "Protect the administrator credential" -> adminaccess; WelcomeIntentsTests holds every entry to a page that exists); then "Or browse by area" with six tiles: "Domains & accounts" -> domains, "Server settings" -> protocols, "Dashboard" -> dashboard, "Live logs" -> logs, "Transport security" -> security, "Backup & restore" -> backup (source: Views/WelcomeView.cs:21-74, 115, 178; Services/WelcomeIntents.cs:45-80)
  • Dashboard: title "Dashboard", subtitle "Live server statistics, refreshed every 2 seconds." (timer 2 s); KPI cards Uptime, Messages processed, In queue (with a "Backlog" badge above StatusSemantics.QueueBacklogThreshold), Spam blocked, Viruses removed; two AccessibleChartCard charts (throughput in messages/minute derived from ProcessedMessages deltas, and SMTP/IMAP/POP3 sessions) with a range selector - Live (the two-second buffer, 90 samples), 24 hours, 7 days, 30 days (the last three load Utilities.GetMetricHistory for processed_messages_total, shown as messages per minute from the difference between buckets, and the three session gauges; a note says when the server keeps no history because MetricsHistoryDays is 0; added 5 September 2026, v6.2.25) - each with a table view and High Contrast palette; a "needs attention" card that runs the same ExternalSetupChecks as the External setup page and links each action item to its owning page, with an "External setup…" button (source: Views/DashboardView.xaml:11-133; Views/DashboardView.xaml.cs:15-66, 103, 160, Refresh(), ShowQueue_(), BeginSetupCheck_(), RenderAttention_())

7.1 Monitoring & troubleshooting

  • Server status (status): cards Server (Version incl. VersionArchitecture, State, Started, Uptime, and - new in 6.2.28 - Update: what the last scheduled or on-demand check found, from Status.UpdateState/AvailableVersion/AvailableVersionPublished/UpdateLastChecked/UpdateLastError, prefixed by the last apply's outcome from Status.UpdateApplyOutcome), Database (Type via Database.DatabaseType 1 MySQL/MariaDB, 2 MS SQL, 3 PostgreSQL, 4 built-in SQL CE; Host; Name; Schema = Database.CurrentVersion), Statistics (processed, spam removed, viruses removed, SMTP/IMAP/POP3 sessions), Warnings; buttons - new in 6.2.28 - "Check for updates" (Status.CheckForUpdate(): reads the release feed now, downloads nothing), "Download update" (Status.DownloadUpdate(): fetches the installer and its Sigstore bundle and keeps it only when verified; enabled once a newer release is known or a failed download can be retried), "Install update" (Status.InstallUpdate() after a confirmation: hands the verified installer to the update helper, the service stops and starts, a service that does not come back is rolled back; enabled only while UpdateState is 3), then "Pause"/"Resume" (Application.Stop()/Start() - the engine stops, the Windows service keeps running) and "Refresh" (source: Views/StatusView.xaml:17-138; Views/StatusView.xaml.cs:42-80, 186-206, 285-297; IDL:540-550)
  • Server status warnings computed client-side: no HostName (High); DenyMailFromNull on (High); any IP range with AllowDeliveryFromRemoteToRemote and not RequireSMTPAuthExternalToExternal (Critical, open relay); 127.0.0.1 range that Expires (High, localhost banned); count of expiring (auto-ban) ranges (Medium) (source: StatusView.xaml.cs LoadWarnings() :224-289)
  • Delivery queue (queue): grid of Id, Created, From, Recipients, NextTry, Tries, File from Status.UndeliveredMessages (tab-separated: id, created, from, recipients, next try, file, locked, tries); buttons "View source" (MessageViewerDialog reads the .eml from disk), "Deliver now" (GlobalObjects.DeliveryQueue.ResetDeliveryTime(id) + StartDelivery()), "Remove" (DeliveryQueue.Remove(id)), "Refresh"; search box (source: Views/QueueView.xaml:17-36; Views/QueueView.xaml.cs:40-137)
  • Message trace (messagetrace): title "Message trace"; Address box + "Search" (GlobalObjects.MessageTrace.Search(address), matches sender or recipient, newest first), "Follow this message" (SearchByQueueID), "Remove expired" (DeleteExpired(), window = MessageTraceRetentionDays); columns Time, Event, Sender, Recipient, Status; queue id 0 = refused before queueing. Initial status text says the trace records nothing unless MessageTraceEnabled is set - off by default (source: Views/MessageTraceView.cs:76-78, 152-234; IDL:2362 GlobalObjects.MessageTrace)
  • Live logs (logs): tails the newest hmailserver_*.log in [Directories] LogFolder read from the LOCAL hMailServer.INI, starting at the last 64 KB, polling every 750 ms, keeping 2000 lines, colour-classified (ERROR/Severity 1-2, SMTPD, IMAPD, POP3D, APPLICATION); "Pause"/"Resume" and "Clear"; needs a local server ("Log folder not found on this machine (live logs need a local server).") (source: Views/LogsView.xaml.cs:18, 54-151)
  • Diagnostics (diagnostics): "Run diagnostics" calls Application.Diagnostics with LocalDomainName (pre-filled with the first hosted domain) and TestDomainName (default gmail.com) then PerformTests() and prints [ OK ]/[FAIL] per test; a "Message-store consistency" card reads the local recovery report (MessageStoreConsistencyReport.FileName in the log folder) written by the server's MessageStoreConsistencyCheck task (start-up + hourly), with "Refresh"/"Open report"; on a remote session it says the number is also published as metric hmailserver_messagestore_missing_files (source: Views/UtilityViews.cs DiagnosticsView ~436-851; IDL:1676 Diagnostics)
  • MX query (mxquery): calls Application.Utilities.ResolveMXRecords(domain) - the server's own resolver, honouring DNSServer in hMailServer.INI (issue #29 replaced an nslookup shell-out); "Query"/"Copy" (source: UtilityViews.cs MxQueryView RunQuery(); IDL:1196)
  • External setup (externalsetup): read-only checklist with four states Done / Not needed / Action needed / Cannot tell, each item linking to its owning page via "Settings…"; DNS TXT checks run in the background via the Windows resolver; items: Windows service account (-> hardening), DKIM public key in DNS (-> domains), ARC inbound filtering trusted sealers (-> antispam), ARC sealing needs a domain DKIM key (-> security), MTA-STS policy hosting (-> webservices), TLS-RPT sender address (-> security), DANE TLSA for MX hosts (-> security), TLS private key passphrase (-> certs), ACME reachability (-> acme), inbound client certificates CA bundle (-> ports), OAuth2/XOAUTH2 key material (-> authentication), client autoconfiguration listener + DNS names (-> webservices) (source: Views/ExternalSetupView.cs:20-68, 153-190; Services/ExternalSetupChecks.cs:504-1493)
  • DNS records (dnsrecords): per hosted domain (combo), generates SPF (from Settings.HostName), DKIM (public key derived from the domain's configured private key file), DMARC (_dmarc.<domain>, starting policy p=none), MTA-STS (what the server will serve, from INI MtaStsHostingEnabled default 1, WebServicesHttpsPort, MtaStsPolicyMode default enforce, MtaStsPolicyMaxAge default 604800, MtaStsPolicyMx) and TLS-RPT (uses TlsRptFromAddress), plus PTR guidance; each record has copy buttons and a "Check" against the Windows resolver with a four-way verdict (not published / different / correct / lookup failed); the MTA-STS policy fetch uses HttpClient with redirects disabled and a 10 s timeout (source: Views/DnsRecordsView.cs:30-85, 971-992, 1258-1261)
  • Logging (logging, ServerSettingsView): one tab with cards "Log categories" (COM Logging.Enabled, LogApplication, LogSMTP, LogIMAP, LogPOP3, LogTCPIP, LogDebug, AWStatsEnabled, KeepFilesOpen, Logging.Device combo "Files on disk"/"Database (SQL)", Logging.LogFormat combo "hMailServer (tab separated)"/"NCSA Common Log Format", INI JsonLogging default 0), "Log detail" (INI LogLevel default 9, MaxLogLineLen default 500, SepSvcLogs default 0), "Log retention" (INI LogDeleteDays default 0), "Message trace" (INI MessageTraceEnabled default off, MessageTraceRetentionDays default 30), "Log files" (shows [Directories] LogFolder and "Open log folder" - local only) (source: ServerSettingsView.xaml.cs BuildLogging :2029-2192)
  • API & monitoring (api, FeatureSettingsView Integration): title "API & monitoring"; cards: "REST administration API + Web Control Deck" (link to apikeys; RestApiPort default 0 = disabled, placeholder 8045; RestApiBindAddress default 127.0.0.1; RestApiCertificateFile, RestApiPrivateKeyFile optional, falls back to the ACME certificate; "TLS is required unless the listener is bound to 127.0.0.1"), "Monitoring" (MetricsServerPort default 0, MetricsHistoryDays default 7 - days of per-minute metric history kept in the database for the dashboard's 24 h/7 d/30 d ranges, 0 = do not record; added 5 September 2026, v6.2.25 - MetricsServerBindAddress default 127.0.0.1, MetricsServerAuthToken, MetricsServerAuthUsername, MetricsServerAuthPassword, MetricsServerCertificateFile, MetricsServerPrivateKeyFile, OtelEndpoint, OtelMetricsEndpoint, OtelLogsEndpoint, OtelMetricsInterval default 60, OtelServiceName default hmailserver, SlowQueryLogMilliseconds default 0) with five live warnings (invalid IPv4 bind, non-loopback bind without credential -> /metrics answers 503, half a Basic pair, half a TLS pair, credential without TLS), "Updates" (new in 6.2.28: UpdateCheckEnabled default 0 - a daily read of the release feed, off until turned on, sends nothing but the request; UpdateCheckHours 24 (1-168); UpdateChannel stable/prerelease; UpdateFeedUrl empty = this project's GitHub releases, set for a mirror; HttpProxy empty = a direct connection, otherwise host:port or [ipv6]:port for the forward proxy every web request the server makes as a client goes through - the feed and its downloads, and the JWKS and token-introspection fetches, which share the same client; CONNECT for an https target with the TLS handshake and the usual certificate verification inside the tunnel, the absolute URL in the request line for plain http, no proxy credentials, and a value without a port refused as an error; UpdateAutoDownload 0; UpdateWindow empty = never apply on its own, e.g. 03:00, Sun 03:00, Sat,Sun 02:00-05:00 - inside it the scheduled task applies a verified installer, stops and starts the service and rolls back one that does not come back; UpdateBackupBeforeApply 1 - no destination or a failed backup means no apply; UpdateRequireAuthenticode 0 - leave off, this project's releases are not Authenticode-signed so 1 refuses every release, the Sigstore check is not optional; UpdateServiceWaitSeconds 180; and five private-Sigstore settings UpdateSourceRepository, UpdateSigningIdentity, UpdateSigningIssuer, UpdateTrustRootsFile, UpdateLogPublicKeyFile - all empty by default, meaning the server trusts exactly one thing: an installer signed by this project's release workflow and recorded in the public transparency log; every setting on the card applies after a service restart), "Windows Event Log" (WindowsEventLogEnabled default 1, WindowsEventLogLevel default 2 with options 1-4), "ManageSieve (RFC 5804)" (ManageSieveServerPort default 0, placeholder 4190; ManageSieveServerBindAddress default 127.0.0.1; live STARTTLS-availability state), "Operability" (ShutdownDrainSeconds default 0) (source: FeatureSettingsView.xaml.cs:1695-1795, 1928-1985, 2037; IniFileSettings.cpp:426, 453, 489-509, 591, 628, 735)
  • REST API keys (apikeys): title "REST API keys"; file-based - reads/creates/revokes hMailServerApiKeys.ini beside hMailServer.INI (one [Key.<id>] section with Label, Expires "yyyy-MM-dd HH:mm:ss", AllowedFrom, Scope, Domains, Hash written LAST); token format hmapi_ + 64 lower-case hex (32 random bytes), only its SHA-256 hex is stored; a created key is shown once ("Copy this key now" card) and cleared on leaving the page; scope fails closed - only the literal "full" is read/write, everything else read-only; default lifetime 90 days, label max 64 chars; the server re-reads the file on every request so no restart; works while the REST listener is off; LOCAL machine only (needs the INI path) (source: Views/ApiKeysView.cs:20-40, 63-70; Services/ApiKeyStore.cs:75-137, 329-340)

Server status: the update line, decoded

New in 6.2.28. Status.UpdateState drives one sentence on the Server card, prefixed by the last apply's outcome when there is one (source: StatusView.xaml.cs UpdateText_ :285-297).

UpdateState The card reads Which buttons are live
0 "Not checked yet (Check for updates, or UpdateCheckEnabled=1 in hMailServer.INI for a daily check)" Check for updates
1 "This is the latest release, checked {when}" Check for updates
2 "{version} is available (published {date})" Check, Download update
3 "{version} is downloaded and verified (published {date}); Install update applies it" Check, Install update
4 "Installing {version}: the service will stop and start" none
5 "The last update step failed: {error}" Check, and Download to retry

Server status: the five configuration warnings

Computed in the Control Panel, not by the server, from Settings and SecurityRanges (source: StatusView.xaml.cs LoadWarnings() :224-289).

Severity Condition Text
Critical an IP range with AllowDeliveryFromRemoteToRemote and without RequireSMTPAuthExternalToExternal "IP range '{name}' allows external-to-external delivery without authentication (open relay risk)."
High Settings.HostName is empty "No public host name is configured in the SMTP settings."
High DenyMailFromNull is on "Mail from an empty sender address is denied. Many servers send bounces from <>, which will be rejected."
High a 127.0.0.1-to-127.0.0.1 range that Expires "Localhost is currently banned in the IP ranges."
Medium any range that Expires the count of auto-ban entries currently in force

External setup: the four states

State Badge word Level Means
Done Done Good the check found what it needed
NotNeeded Not needed Normal the feature is off, so nothing outside is required
CannotTell Cannot tell Information most often a remote session, or a DNS lookup that failed
ActionNeeded Action needed Warning something must be done outside this server

An item's state is the worst of its findings (one per domain, port or certificate), and the enum is ordered so that aggregating is Max. The word is always printed - never only a colour or a shape. The twelve items are: Windows service account; DKIM signing; ARC inbound filtering; ARC sealing of forwarded mail; MTA-STS policy hosting; TLS reporting (TLS-RPT); DANE for inbound mail; TLS private keys; ACME certificates; Inbound client certificates; OAuth2 / XOAUTH2; Client autoconfiguration (source: ExternalSetupChecks.cs:16-21, 155-181, and the twelve Title = L(...) items).

7.2 Accounts & domains

  • Domains (domains): on-screen "Domains & accounts"; left list of domains (search, "Properties", "Add domain" with box newdomain.com), accounts of the selected domain (search, "Edit", "Delete", create with address + password + "Generate" + "Create account"), Aliases panel (alias@domain -> target@anywhere, "Add"/delete), Distribution lists panel ("Properties", "Members", "Add"); all via Application.Domains (source: Views/DomainsView.xaml:16-272; Views/DomainsView.xaml.cs)
  • Domain dialog: title "Domain - {name}"; tabs General, Names, Limits, Signature, Relay, Out of office, DKIM; writes Active, Name, Postmaster, ADDomainName (Active Directory link), MaxSize, MaxMessageSize, MaxAccountSize, MessageRetentionDays (Limits tab, "Delete messages in this domain's mailboxes older than (days; 0 = no policy, an account's own value overrides it)", added 5 September 2026 on schema 6027), MaxNumberOfAccounts(+Enabled), MaxNumberOfAliases(+Enabled), MaxNumberOfDistributionLists(+Enabled), PlusAddressingEnabled/Character, SignatureEnabled/Method/PlainText/HTML, RelayHost/Port/RequiresAuthentication/Username/Password/ConnectionSecurity, VacationMessageIsOn/VacationSubject/VacationMessage/VacationInternalSubject/VacationInternalMessage/VacationExternalOverride, DKIMSignEnabled/Selector/PrivateKeyFile/SecondarySelector/SecondaryPrivateKeyFile/SigningAlgorithm/HeaderCanonicalizationMethod/BodyCanonicalizationMethod/SignAliasesEnabled; the DKIM tab generates keys ("Save the DKIM private key" / "Save the new DKIM private key" file dialogs) and shows the DNS record; the Names tab embeds the Domain aliases collection editor (source: Views/DomainDialog.cs:7-10, 130-161, 216, 617; property grep; Views/CollectionSpecs.cs:184-201)
  • Account dialog: title "Account - {address}"; tabs General, Forwarding, Auto-reply, Spam, Signature, Sieve, External, App passwords, Two-factor, Rules, Folders, Directory; General has Address, Administration level (Normal user 0 / Domain administrator 1 / Server administrator 2), Quota (MB), "Delete messages older than (days; 0 = the domain's policy, -1 = keep forever)" (MessageRetentionDays, added 5 September 2026 on schema 6027), first/last name, New password with "Generate strong password" and a strength meter, Last logon; writes Active, Address, AdminLevel, MaxSize, MessageRetentionDays, PersonFirstName/LastName, Password, ForwardEnabled/Address/KeepOriginal/AbortSpamFlagged, VacationMessageIsOn/VacationMessage/VacationMessageExpires/ExpiresDate/BeginDate/AbortSpamFlagged, SpamMarkThreshold/SpamDeleteThreshold (per-account), SignatureEnabled/PlainText/HTML, SieveScript, ADDomain/ADUsername (source: Views/AccountDialog.cs:114-150, 183-200; property grep)
  • Account "External" tab = FetchAccounts collection editor, title "External accounts": Enabled, Name, ServerType combo 0 "POP3 - download and (unless kept for N days) delete" / 1 "IMAP - the INBOX, collected once by UID; kept on the server unless Days to keep is 0" (added 5 September 2026, v6.2.25), ServerAddress, Port (default 110), Username/Password, MinutesBetweenFetch (15), DaysToKeepMessages (0), ConnectionSecurity combo 0 None(110)/1 SSL-TLS(995)/2 STARTTLS optional/3 STARTTLS required (replaces the old UseSSL checkbox, whose getter was ConnectionSecurity == CSSSL and whose setter wrote CSSSL or CSNone - so an account already set to STARTTLS read back as unticked and was rewritten to implicit TLS on the next save, which does not connect on port 110), UseAntiSpam (default on), UseAntiVirus (default on), and MirrorFolders "Mirror every folder (IMAP only: a migration, verbatim, with flags and dates)" default off, ignored for POP3 (added in v6.2.27 on schema 6031) (source: CollectionSpecs.cs:31-44, 205-259)
  • Account "App passwords" tab: account.AppPasswords collection; columns Name, State, Issued, Last used; buttons Create, Revoke / restore, Delete, Refresh; a generated password is shown once (server stores only a hash) (source: Views/AppPasswordsPanel.cs:7-22, 156, 194-255)
  • Account "Two-factor" tab: per-mailbox TOTP enrolment with a QR code shown once; enrolling means the account password stops authenticating mail clients, so app passwords are required (source: Views/AccountTwoFactorPanel.cs:7-20)
  • Account "Rules" tab: per-account rules (add/rename/enable/delete; criteria and actions via the embedded RulesView) (source: CollectionSpecs.cs:254-272; RulesView.xaml.cs ConfigureForRules)
  • Distribution list dialog: title "Distribution list - {address}"; writes Active, Address, Mode, RequireSMTPAuth, RequireSenderAddress, ModeratorAddress, BounceAddress; members edited in "Recipients - {list}" dialog (source: Views/DistributionListDialog.cs:35; Views/RecipientsDialog.cs:31; property grep)
  • "Browse Active Directory" dialog: lists forest domains, searches users (Account/Name/E-mail), single or multi select; used to fill an account's AD fields and to bulk-import members (source: Views/ActiveDirectoryPickerDialog.cs:54, 188-190)
  • Public folders (publicfolders): lists Settings.PublicFolders; "Add folder" (prompt), "Permissions" (double-click too), "Delete" (confirms "and all messages in it"), "Refresh" (source: Views/PublicFoldersView.cs:42-165)
  • Folder permissions dialog "Permissions - {folder}": ACL entries per account or group with eACLPermission bits Lookup 1, Read 2, ..., Insert/append 16, Post 32 - labelled "Post - and send as this mailbox's address over SMTP (Send-As, when SmtpAuthenticatedSenderCheck is on)" since 5 September 2026 (v6.2.25) - ..., Expunge 512, Administer 1024 (source: Views/FolderPermissionsDialog.cs:66-79, 95, 263-267)
  • Groups (groups): Settings.Groups list plus a "Members…" button opening "Members - {group}" (resolves member AccountIDs to addresses; "Add account to group") - the member editor did not exist before, so groups granted folder rights to nobody (source: CollectionSpecs.cs:128-147, 285-386; Views/GroupMembersDialog.cs:49, 300; IDL:655)

7.3 Mail flow & delivery

  • Delivery of e-mail (delivery): tabs Delivery (COM SMTPNoOfTries, SMTPMinutesBetweenTry, MaxNumberOfMXHosts, INI MXTriesFactor default 0, COM SMTPDeliveryBindToIP, SMTPConnectionSecurity combo, AddDeliveredToHeader, INI QuickRetries 0, QuickRetriesMinutes 6, QueueRandomnessMinutes 0, MaxOutboundPerDestinationPerMinute 0, and - editors new in 6.2.28 for three keys that shipped in 6.2.25 - INI OutboundPipelining default on (send a transaction's commands without waiting for each answer, only to a server that advertises PIPELINING), OutboundChunking default on (BDAT instead of dot-stuffed DATA; also what relays a BINARYMIME message onward unchanged), DeliveryHardLinks default off (one file linked per local recipient instead of copied; needs the data directory on NTFS, otherwise the server copies and says so in the SMTP log); all three apply after a service restart), Relayer ("SMTP relayer (smart host)": SMTPRelayer with host1|host2 failover, SMTPRelayerPort, SMTPRelayerConnectionSecurity, SMTPRelayerRequiresAuthentication, SMTPRelayerUsername, password via SetSMTPRelayerPassword; card "OAuth2 for the relay (Microsoft 365)": INI OutboundOAuth2Hosts default smtp.office365.com, OutboundOAuth2TokenUrl, OutboundOAuth2ClientId, OutboundOAuth2ClientSecret, OutboundOAuth2Scope, OutboundOAuth2FixedToken, FetchOAuth2Hosts default outlook.office365.com), Rules (RuleLoopLimit) (source: ServerSettingsView.xaml.cs BuildDelivery :1218-1356, the three INI rows at :1228-1252; IniFileSettings.cpp:312, 732-733)
  • Routes (routes): grid Domain/Target host/Port/Retries/Auth from Settings.Routes; inline add (domain, host, port default 25; NumberOfTries 3, MinutesBetweenTry 10), "Edit" opens "Route - {domain}" with tabs General, Delivery, Addresses, Security, Authentication writing TargetSMTPHost/Port, Description, NumberOfTries, MinutesBetweenTry, AllAddresses, TreatSenderAsLocalDomain, TreatRecipientAsLocalDomain, ConnectionSecurity, RelayerRequiresAuth, RelayerAuthUsername, route addresses (source: Views/RoutesView.xaml.cs; Views/RouteDialog.cs:49-70; property grep)
  • Incoming relays (relays): title "Incoming relays", subtitle "Upstream gateways (spam filters, load balancers) whose IP addresses should not count as the connecting client in anti-spam host checks."; Settings.IncomingRelays Name/LowerIP/UpperIP add/delete (source: UtilityViews.cs:15-33, 100-185)
  • Rules (rules): on-screen "Global rules", subtitle "Server-wide message rules, evaluated top to bottom."; Application.Rules; grid #/Name/Enabled; buttons Move up, Move down, Enable / disable, Delete rule, Add rule; IF criteria (Add…/Edit…/Remove) and THEN actions (Add…/Edit…/Remove/Up/Down) (source: Views/RulesView.xaml:18-108; Views/RulesView.xaml.cs:31, 249-420)
  • Rule action types offered: Delete e-mail 1, Forward e-mail 2, Reply 3, Move to IMAP folder 4, Run script function 5, Stop rule processing 6, Set header value 7, Send using route 8 (server-level only), Create copy 9, Bind to address 10 (server-level only) (source: Views/RuleActionDialog.cs:73-84)
  • Server messages (servermessages): Settings.ServerMessages Name/Text, fixed set (no add/delete) (source: CollectionSpecs.cs:149-162)
  • Server sendout (sendout): wildcard (default *), from address/name, subject, body -> Utilities.EmailAllAccounts(...); reports "The server did not queue the sendout" when the call returns false (source: UtilityViews.cs SendoutView; IDL:1177)

7.4 Spam & virus filtering

  • Spam filtering overview (spamoverview): read-only; reads Settings.AntiSpam thresholds, headers, HELO/PTR/MX checks and scores, SPF/DKIM/DMARC and scores, SpamAssassin, greylisting and bypasses, blocked-sender count; judgement in Services/SpamPipeline.cs; every row links to the owning page; "Refresh" (source: Views/SpamOverviewView.cs:21-40, 126-156)
  • Quarantine (quarantine): Settings.AntiSpam.Quarantine (Refresh(), Count, indexer pages the table); columns Held, Sender, Recipients, Score, Subject, row tooltip "Held because: {reason}"; buttons "Release" (ReleaseByDBID), "Delete" (DeleteByDBID, confirmation defaults to No - the only copy), "Remove expired" (DeleteExpired(), window QuarantineRetentionDays), "Refresh"; empty state text says quarantining is off unless QuarantineEnabled is set (source: Views/QuarantineView.cs:14-33, 87-166, 241-360; IDL:2595 "Empty and inert unless QuarantineEnabled is set in hMailServer.ini")
  • Anti-spam settings (antispam): on-screen "Anti-spam"; tabs General ("Thresholds & actions": AntiSpam.SpamMarkThreshold, SpamDeleteThreshold, AddHeaderSpam, AddHeaderReason, PrependSubject, PrependSubjectText, MaximumMessageSize; "Quarantine instead of refusing": INI QuarantineEnabled off by default, QuarantineRetentionDays default 30; "Recipient tarpit": COM AntiSpam.TarpitCount - recipients a message may name before the delay starts, 0 = off - and AntiSpam.TarpitDelay - seconds each further RCPT TO in an unauthenticated session waits, at most 30; stored as INI SmtpTarpitCount / SmtpTarpitDelaySeconds, takes effect at once, authenticated sessions and IP ranges with spam protection off are exempt; added 5 September 2026, v6.2.25), Sender auth (UseSPF+UseSPFScore, INI SpfVoidLookupLimit, DKIMVerificationEnabled+DKIMVerificationFailureScore, INI DkimAcceptSha1 off, DMARCEnabled+DMARCFailureScore, INI DmarcTreeWalkEnabled, INI DmarcRptSchemaVersion default 1, COM ArcFilteringEnabled + ArcTrustedSealers - "does nothing at all" with an empty sealer list), Host checks (CheckHostInHelo(+Score), CheckPTR(+Score), UseMXChecks(+Score)), Greylisting (GreyListingEnabled, GreyListingInitialDelay, GreyListingInitialDelete and FinalDelete shown in days (stored hours, Divisor 24), BypassGreylistingOnMailFromMX, BypassGreylistingOnSPFSuccess, INI GreylistingEnabledDuringRecordExpiration default 1, GreylistingRecordExpirationInterval default 240, button "Clear greylisting triplets"), SpamAssassin (SpamAssassinEnabled/Host/Port/MergeScore/Score, "Test SpamAssassin connection", INI SAMinTimeout 30, SAMaxTimeout 90, SAMoveVsCopy off, SpamAssassinUser empty and SpamAssassinUserFromRecipient off - the spamd User: header, applied on Reinitialize (added 5 September 2026); the editor new in 6.2.28 for a key that shipped in 6.2.25: INI SpamAssassinLearnOnMove default off - moving a message into the folder designated Junk reports it to sa-learn as spam and moving it back out as ham, one sa-learn run per move, applies after a service restart; card "External filtering engine (rspamd and anything else that speaks HTTP)": INI FilterHookUrl (empty = none), FilterHookTimeoutSeconds 10, FilterHookFailClosed off, FilterHookRejectScore 100, FilterHookMaxMessageSizeKB 10240) (source: ServerSettingsView.xaml.cs BuildAntiSpam :1357-1610, tarpit card :1374-1378, learn-on-move :1558-1566; IDL:2583-2586; IniFileSettings.cpp:354, 676-679)
  • SURBL servers (surbl): AntiSpam.SURBLServers Active/DNSHost/RejectMessage/Score (default 5) (source: CollectionSpecs.cs:38-51)
  • DNS blacklists (dnsbl): AntiSpam.DNSBlackLists Active/DNSHost/ExpectedResult/RejectMessage/Score (default 5) (source: CollectionSpecs.cs:53-67)
  • White list (spamwhitelist): on-screen "Anti-spam white list"; AntiSpam.WhiteListAddresses LowerIPAddress/UpperIPAddress/EmailAddress/Description (source: CollectionSpecs.cs:69-82)
  • Blocked senders (blockedsenders): AntiSpam.BlockedSenders Address (exact with @, else domain + subdomains)/Score default 100/Description; matches the CLAIMED sender - not anti-spoofing (source: CollectionSpecs.cs:84-100)
  • Greylisting white list (greylistwhitelist): AntiSpam.GreyListingWhiteAddresses IPAddress/Description (source: CollectionSpecs.cs:102-113)
  • Virus scanning overview (virusoverview): read-only; reads ClamAV/ClamWin/custom scanner settings; exists because VirusScanner::ScanFile_ treats "every scanner errored" as NoVirusFound, so an enabled-but-broken scanner looks like a clean scan; judgement in Services/VirusPipeline.cs (source: Views/VirusOverviewView.cs:21-44, 131-139)
  • Anti-virus settings (antivirus): on-screen "Anti-virus"; tabs General (AntiVirus.Action combo, NotifySender, NotifyReceiver, MaximumMessageSize, EnableAttachmentBlocking; card "When a scanner cannot run": INI AVFailAction 0 deliver / 1 hold (default 0), AVFailRetryMinutes 15, AVFailMaxHolds 16), ClamAV (ClamAVEnabled/Host/Port, "Test ClamAV connection", INI ClamMinTimeout 15, ClamMaxTimeout 90), ClamWin (ClamWinEnabled/Executable/DBFolder, "Auto-detect ClamWin", "Test ClamWin scanner"), Custom (CustomScannerEnabled/Executable/ReturnValue, preset picker, "Test custom scanner") (source: ServerSettingsView.xaml.cs BuildAntiVirus :1667-1777)
  • Anti-virus "Test ClamAV connection" (5 September 2026): the server asks clamd nPING (expects PONG) and nVERSION before scanning, so the result names the daemon ("ClamAV 1.5.4/28085/..."), a port with something else on it is reported as not answering PING, and both samples (plain, EICAR) are streamed from memory - the EICAR file used to be written to the data directory, where Windows Defender removed it before clamd saw it and the button reported a missing file. The ClamWin and custom-scanner tests still write a file and say so when a host antivirus removes it first (source: VirusScannerTester.cpp; ClamAVVirusScanner::ScanData)
  • Blocked attachments (blockedattachments): AntiVirus.BlockedAttachments Wildcard (default *.exe)/Description; requires AntiVirus.EnableAttachmentBlocking on the Anti-virus page (source: CollectionSpecs.cs:115-126)

7.5 Connections & protocols

  • Protocols (protocols): tabs Services (COM ServiceSMTP, ServiceIMAP, ServicePOP3), SMTP (HostName, MaxSMTPConnections, MaxMessageSize, MaxSMTPRecipientsInBatch, WelcomeSMTP, AllowSMTPAuthPlain, DenyMailFromNull, INI RejectFullMailboxAtRcpt default on, INI QuotaWarningPercent default 90, INI ArchiveRetentionDays default 0, INI MetricsPerDomainEnabled default off, AllowIncorrectLineEndings, DisconnectInvalidClients, MaxNumberOfInvalidCommands, INI SmtpAuthenticatedSenderCheck default off - "An authenticated session may only send as an address its account owns or has been granted": MAIL FROM must be the account's own address, an alias resolving to it, or a mailbox whose owner granted the post (p) right on its INBOX as the Send-As grant, otherwise 550 5.7.1; added 5 September 2026, v6.2.25), IMAP (MaxIMAPConnections, WelcomeIMAP, IMAPIdleEnabled, IMAPQuotaEnabled, IMAPSortEnabled, IMAPACLEnabled, IMAPSASLPlainEnabled, IMAPSASLInitialResponseEnabled, IMAPPublicFolderName, IMAPMasterUser, IMAPHierarchyDelimiter, CreateDefaultSpecialUseFoldersEnabled default off - creates Drafts, Sent, Trash and Junk with their RFC 6154 designation when an account is created; they survive Account.DeleteMessages and go with the account; added 5 September 2026, schema 6026; new in 6.2.28: INI IMAPCompressionEnabled default on - lets a client ask for the rest of the session to be compressed (COMPRESS=DEFLATE, RFC 4978); a client that does not ask is unaffected; applies after a service restart; card "IMAP search limits": INI IMAPSearchTimeout 60, IMAPSearchMaxMegabytes 2048; card "Change history for synchronising clients": INI IMAPExpungeRetentionRecords 5000; card "Repair": button "Recalculate folder UID counters"), POP3 (MaxPOP3Connections, WelcomePOP3; card "How often a client may check for mail": INI Pop3LoginDelaySeconds 0), Timeouts (INI SMTPDMinTimeout 10, SMTPDMaxTimeout 1800, SMTPCMinTimeout 30, SMTPCMaxTimeout 600, POP3DMinTimeout 10, POP3DMaxTimeout 600, POP3CMinTimeout 30, POP3CMaxTimeout 900) (source: ServerSettingsView.xaml.cs BuildProtocols :1038-1217, SmtpAuthenticatedSenderCheck :1067, IMAPCompressionEnabled :1109-1116; IniFileSettings.cpp:497, 641, 643, 656)
  • TCP/IP ports (ports): grid of Settings.TCPIPPorts Protocol (1 SMTP/3 POP3/5 IMAP), Address, Port, Security (None / SSL/TLS / STARTTLS (optional) / STARTTLS (required)), Certificate name, and a live "Listening"/"Not listening"/"Server off"/"Unknown" badge from the local TCP table (ListenerProbe, local sessions only) with a summary line naming unbound ports; inline add (protocol, address, port, security), "Edit" opens "TCP/IP port" dialog (writes Protocol, Address, PortNumber, ConnectionSecurity, SSLCertificateID, ClientCertificatePolicy, ClientCertificateCAFile - mutual TLS), "Restore defaults" = TCPIPPorts.SetDefault() (SMTP 25 + 587, POP3 110, IMAP 143, no security), Delete (source: Views/TcpIpPortsView.xaml.cs:15-34, 55-175, 291-323; Views/TcpIpPortDialog.cs:13-36; property grep)
  • Web services & autoconfiguration (webservices): on-screen "Web services & client autoconfiguration"; cards "Listener" (WebServicesHttpPort default 0, WebServicesHttpsPort default 0 - "Nothing below is served until a port is set here", WebServicesBindAddress default 0.0.0.0, WebServicesCertificateFile/WebServicesPrivateKeyFile optional, fall back to ACME), "Client autoconfiguration (autoconfig & autodiscover)" (AutoconfigEnabled default 1, AutoconfigClientHost), "MTA-STS policy hosting" (MtaStsHostingEnabled default 1, MtaStsPolicyMode default enforce, MtaStsPolicyMaxAge default 604800, MtaStsPolicyMx), "Calendar and contacts discovery (CalDAV / CardDAV)" (CalDavRedirectUrl, CardDavRedirectUrl - redirects only; "hMailServer does NOT implement CalDAV or CardDAV") (source: FeatureSettingsView.xaml.cs:2668-2760; IniFileSettings.cpp:602-611)
  • DNS resolver (dns): cards "Name servers" (DNSServer - single IPv4, port 53; empty = Windows resolvers), "DNS cache" (UseDNSCache default 1 - toggles DNS_QUERY_BYPASS_CACHE, i.e. Windows' cache, ignored when DNSServer is set), "DNS blacklist checks" (DNSBLChecksAfterMailFrom default 1) (source: FeatureSettingsView.xaml.cs:2624-2666; IniFileSettings.cpp:249, 400)

7.6 TLS & certificates

  • SSL certificates (certs): grid of Settings.SSLCertificates Name, Certificate file, Private key file, Certificate state (parses, expiry, days remaining), Private key state (exists, encrypted, matches the certificate) via CertificateInspector off the UI thread - honest "cannot be checked from here" on a remote session; "Private key passphrase" card (COM PrivateKeyPassword, DPAPI-protected, write-only; needed for an encrypted key or the TLS ports do not start - server error 6170) with "Save passphrase"/"Remove stored passphrase"; "Add certificate" (name, PEM cert file, PEM key file, optional passphrase), "Delete selected"; ACME-issued certificates appear here automatically (source: Views/SslCertificatesView.xaml:18-120; Views/SslCertificatesView.xaml.cs:14-40, 103-155, 384-667)
  • Certificates (ACME) (acme): on-screen "Automatic certificates (ACME)"; card "ACME (Let's Encrypt)": AcmeEnabled default 0, AcmeContactEmail, AcmeDomains, AcmeDirectoryUrl default https://acme-v02.api.letsencrypt.org/directory, AcmeHttpPort default 80, AcmeCertificateDirectory (empty = Data\ACME), AcmeReuseKey default 1; a live warning reads the issued certificate's expiry from disk (source: FeatureSettingsView.xaml.cs:1759-1792; IniFileSettings.cpp:595)
  • Transport encryption overview (tlsoverview): read-only; reads TlsVersion10..13Enabled, SslCipherList, VerifyRemoteSslCertificate, the ports and certificates; judgement in Services/TlsPosture.cs; flags e.g. a TLS port with no certificate (fails to start, no plaintext fallback), AEAD-ONLY with TLS 1.0/1.1 enabled (no usable suite), expired certificates, plaintext-password exposure (the IP-range "require TLS for auth" control) (source: Views/TlsOverviewView.cs:21-40, 132-142; NavigationMap.cs comment on tlsoverview)
  • SSL/TLS (tls): tabs Protocol versions (COM TlsVersion10Enabled, TlsVersion11Enabled, TlsVersion12Enabled, TlsVersion13Enabled), Ciphers (SslCipherList - "TLS 1.2 and below (OpenSSL format)", the single value AEAD-ONLY is a named preset; INI TlsCipherSuites13 for TLS 1.3 RFC 8446 names; TlsOptionPreferServerCiphersEnabled; TlsOptionPrioritizeChaChaEnabled - only effective with server order + TLS 1.2/1.3, wired live; VerifyRemoteSslCertificate; the editor new in 6.2.28 for a key that shipped in 6.2.24: INI TlsKeyExchangeGroups - the elliptic-curve and finite-field groups the server will agree a key with, in OpenSSL group-list syntax; the editor is pre-filled with the list the server ships, X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:secp256r1, because a blank saved back would silently drop the two post-quantum hybrids; an empty list, or one OpenSSL rejects, is logged and the classical fallback secp384r1:x25519:secp256r1 used instead; applies after a service restart), Session resumption (INI TlsSessionTicketsEnabled default 1, TlsSessionCacheSize default 0 (-1 = no cache), TlsSessionTimeoutSeconds default 0, TlsTicketKeyRotationSeconds default 0) (source: ServerSettingsView.xaml.cs BuildTls :1719-1843, TlsKeyExchangeGroups :1736-1743; IniFileSettings.cpp:584, 687)
  • Transport security (security): cards "DANE & DNSSEC" (DaneEnforcementEnabled default 1, DnssecValidationEnabled default 1, DnssecTrustAnchors), "MTA-STS" (MtaStsEnabled default 1 - honouring OTHER domains' policies), "ARC sealing" (ArcSealingEnabled default 0; needs a hosted domain with DKIM or it "reads enabled and seals nothing"), "ARC inbound filtering" (information only - points to the Anti-spam page), "DKIM signature timestamps" (DKIMSignatureValiditySeconds 0, DKIMEnforceSignatureExpiry 1, DKIMExpiryClockSkewSeconds 300), "DKIM oversigning" (DkimOversignHeaders empty = off), "Authentication results on inbound mail" (AuthenticationResultsEnabled 0, ReceivedSpfHeaderEnabled 0, AuthenticationResultsIdentity), "TLS reporting (TLS-RPT)" (TlsRptFromAddress empty = disabled, TlsRptOrganizationName default hMailServer), "DMARC aggregate reporting (rua)" (DmarcRptFromAddress empty = disabled, DmarcRptOrganizationName), "Forwarded mail & bounce protection (SRS / BATV)" (RewriteEnvelopeFromWhenForwarding 0, SRSEnabled 0 + SRSSecret, BATVEnabled 0 + BATVSecret; live warnings when enabled with no secret - SRS-on-no-secret also suppresses the plain rewrite) (source: FeatureSettingsView.xaml.cs:1470-1757; IniFileSettings.cpp:402-404, 455, 480, 614-616)

7.7 Access & abuse protection

  • Authentication (authentication): cards "OAuth2 / external identity provider" (OAuth2Enabled default 0, OAuth2RequireTLS 1, OAuth2Issuer, OAuth2Audience, OAuth2AllowedAlgorithms default RS256, OAuth2UsernameClaim default email, OAuth2PublicKeyFile, OAuth2HmacSecret, and since 5 September 2026 OAuth2JwksUrl, OAuth2JwksCacheSeconds 3600, OAuth2IntrospectionUrl, OAuth2IntrospectionClientId, OAuth2IntrospectionClientSecret, OAuth2IntrospectionCacheSeconds 300, OAuth2IntrospectionFailOpen off; live coherence warning - the mailbox must still exist locally), "Password storage" (PreferredHashAlgorithm default 4 with options 5 Argon2id / 4 PBKDF2 / 3 SHA-256 / 2 MD5 / 1 Blowfish; MinimumAcceptedHashAlgorithm default 0 with 0/3/4/5; PasswordPepper - changing it invalidates ALL existing passwords; INI PasswordHashIterations 0, PasswordHashMemoryKB 0, PasswordHashTimeCost 0 - work factors for new hashes, 0 = the built-in default, bounded; a stored hash cheaper than the configured value is re-derived on the next logon, never a costlier one; added 5 September 2026), "SMTP authentication" (DisableAUTHList - ports where AUTH is not offered) (source: FeatureSettingsView.xaml.cs:2539-2622; IniFileSettings.cpp:171, 208)
  • Directory authentication (LDAP) (ldap): edits the [LDAP] section of the LOCAL hMailServer.INI directly (ProfileApi, not IniFeatureStore); keys Enabled (default 0), Server, Port (0 = protocol default), Security (combo: 2 "LDAPS - TLS from the first byte (default, port 636)", 1 "StartTLS - connect on 389, upgrade to TLS before anything is sent", 0 "Unprotected LDAP - cleartext (lab networks only)"; server default 2, an unrecognised value is read as LDAPS), VerifyCertificate (1), AllowUnprotectedPassword (0), BindMethod (combo: 0 "Simple bind - the password is sent to the directory (use with TLS)", 1 "Negotiate (Kerberos/NTLM) - the password never crosses the network"; default 0), SearchBase, UserSearchFilter (default (&(objectCategory=person)(objectClass=user)(sAMAccountName=%u))), UserDnTemplate, ServiceUsername, ServicePassword (write-only, clear via checkbox), TimeoutSeconds (10, page allows 1-90), FallbackToWindowsLogon (0); cards "Is this configuration complete?", "Directory server", "How the password is proved", "Finding the user's directory entry", "Service account (search mode only)", "If the directory cannot answer", "What has to exist outside this server", "Test the connection" (user name + AD domain + password); the server re-reads [LDAP] within two seconds of a save - no restart (source: Views/LdapSettingsView.cs:23-60, 345-624, 715-866; hmailserver/source/Server/Common/LDAP/LdapSettings.cpp:17, 229-276)
  • Directory synchronisation (directorysync): provisions mailboxes from LDAP for domains whose ADDomainName is set; INI [LDAP] keys SyncFilter, SyncMailAttribute (default mail), SyncUsernameAttribute (sAMAccountName), SyncDisplayNameAttribute (displayName), SyncMaxUsers (5000), SyncScheduleMinutes (0 = no unattended run); "Preview" -> Settings.PreviewDirectorySync(domain, disableMissing, out text) and "Apply" -> Settings.ApplyDirectorySync(...) run inside the server; Apply is disabled until a Preview has run with identical options; "mark absent accounts inactive" is part of the preview; never deletes accounts or messages (source: Views/DirectorySyncView.cs:19-50, 96-125, 215-386, 498-507, 799-809; LdapSettings.cpp:54-62, 278-322)
  • Administrative access (adminaccess): tabs Password ("Administrator password" via SetAdministratorPassword - used by the Control Panel, the REST API and COM scripts, stored hashed in hMailServer.ini; "Policy for mailbox passwords": INI PasswordPolicyMinimumLength 0, PasswordPolicyRequireMixedCase, PasswordPolicyRequireDigit, PasswordPolicyRequireNonAlphanumeric, PasswordPolicyRejectCommon - all off, applied when a password is CHOSEN, never at logon; "Reuse and expiry": PasswordPolicyHistoryCount 0, PasswordPolicyMaximumAgeDays 0 - no self-service reset exists, AD accounts exempt), Two-factor (button opening TotpSetupDialog) (source: ServerSettingsView.xaml.cs BuildAdminAccess :2568-2737)
  • Auto-ban (autoban): card "Auto-ban" (COM AutoBanOnLogonFailure, MaxInvalidLogonAttempts - 0 disables even when ticked, MaxInvalidLogonAttemptsWithin - record lifetime, not a sliding window, AutoBanMinutes - 0 drops the connection but creates no range; button "Clear logon-failure list" = Settings.ClearLogonFailureList() which does NOT lift existing bans - delete the "Auto-ban: {user}" range (priority 100) on the IP ranges page), card "Per-name lockout" (INI AccountLockoutThreshold 0 = off, AccountLockoutWindowMinutes 30, AccountLockoutMinutes 30; counted per user name across SMTP AUTH, POP3, IMAP, ManageSieve, REST), card "Logon tarpit" (INI LogonTarpitSeconds 0 = off; the first wrong password on a connection waits this many seconds, the second twice as long and so on up to 30 s, on SMTP, POP3 and IMAP alike; a correct password is never delayed; the wait is a timer on the connection, not a thread asleep; 2 is a reasonable start; applies after a service restart; added 5 September 2026, v6.2.25) (source: ServerSettingsView.xaml.cs BuildAutoBan :1844-1949 and the doc comment above it; IDL:666; IniFileSettings.cpp:675)
  • IP ranges (ipranges): grid of Settings.SecurityRanges (Name, Lower/Upper IP, Priority, SMTP/IMAP/POP3 allowed); inline permissions panel (Allow SMTP/IMAP/POP3, RequireSSLTLSForAuth, the four AllowDeliveryFrom{Local,Remote}To{Local,Remote} flags) with "Save"; inline add (name, lower, upper, priority default 15; all three protocols allowed); "Edit" opens "IP range" dialog with tabs General, Connections, Relaying, Require auth (RequireSMTPAuthLocalToLocal/LocalToExternal/ExternalToLocal/ExternalToExternal), Protection (EnableSpamProtection, EnableAntiVirus, Expires, ExpiresTime); "Restore defaults" = SecurityRanges.SetDefault() ("My computer" 127.0.0.1 priority 30 and "Internet" 0.0.0.0-255.255.255.255 priority 10 - removes auto-ban entries too) (source: Views/IPRangesView.xaml.cs:38-287; Views/IPRangeDialog.cs:13-78; property grep; IDL:1496)

7.8 Maintenance

  • Backup & restore (backup): COM Settings.Backup Destination, BackupDomains, BackupMessages, BackupSettings, CompressDestinationFiles (setters persist immediately - no Save method); INI BackupMessagesDBOnly (local only); checkbox "Verify each backup by extracting its message store and reconciling it against the rows" = INI BackupVerifyRestore (default on), read and written with BackupMessagesDBOnly, disabled with an explanation when hMailServer.ini is not on the machine (added 5 September 2026, v6.2.25); "Start backup now" = Application.BackupManager.StartBackup(); Restore: pick HMBackup*.xml on the server, RestoreDomains/Messages/Settings, BackupManager.LoadBackup(file).StartRestore(); "Scheduled backups" card (local INI only): ScheduledBackupTime (strict 24-hour HH:MM, empty = off), ScheduledBackupIntervalMinutes (0), ScheduledBackupKeepCount (0 = keep all), ScheduledBackupMaxAgeDays (0) - daily time wins over interval; status lines say whether a schedule exists and the age of the newest HMBackup YYYY-MM-DD HHMMSS.7z archive in the destination, Critical when older than twice the expected interval (source: Views/BackupView.xaml:11-124; Views/BackupView.xaml.cs:15-96, 141-244, 313-420; IDL:1672)
  • Performance (performance): tabs Threads (COM MaxDeliveryThreads, MaxAsynchronousThreads, TCPIPThreads, INI MaxNumberOfExternalFetchThreads 15, WorkerThreadPriority shown INERT), Cache (Cache.Enabled, four *CacheTTL, four *CacheMaxSizeKb (session-only, reset to 10240 KB at restart), live "How the caches are performing" hit-rate/memory readout from Cache.*HitRate), Indexing (MessageIndexing.Enabled, INI IndexerFullMinutes 720, IndexerFullLimit 25000, IndexerQuickLimit 1000, IndexerFullText 0 - needs indexing on, IndexerFullTextBatchSize 250, IndexerFullTextMinTokenLength 3, IndexerFullTextMaxTokensPerMessage 2048; buttons "Index now, and show how far behind it is", "Discard the index and rebuild it"), Database (LOCAL INI [Database] NumberOfConnections 5 - forced to 1 on MSSQLCE and labelled so, ConnectionAttempts 6, ConnectionAttemptsDelay 5; [Settings] DatabaseStatementTimeout 30) (source: ServerSettingsView.xaml.cs BuildPerformance :2213-2466; IDL:2243-2257; IniFileSettings.cpp:365)
  • Advanced (advanced; "Advanced & scripting" until 5 September 2026): on-screen "Advanced"; tabs General (DefaultDomain, IPv6PreferredEnabled, UserInterfaceLanguage - third-party COM tools only), Copies of mail ("Mirroring" MirrorEMailAddress; "Message archiving" INI ArchiveDir (empty = off), ArchiveHardLinks 0, ArchiveDomains "Only archive mail for these domains (comma-separated; empty = every message)" (added 5 September 2026, v6.2.25); "Disk space" INI DiskSpaceWarningThresholdMB 1024, MinimumFreeDiskSpaceMB 100), Scripting (Scripting.Enabled, Scripting.Language VBScript/JScript; engine reloads on Save; the editor new in 6.2.28 for a key that shipped in 6.2.25: INI ScriptAllowedObjects - the COM program identifiers an event script may instantiate besides hMailServer's own objects, comma separated (e.g. a scripting file system object or an HTTP client). The editor is pre-filled with *, which is what the server reads when the key is absent, so saving the page cannot turn "any object, as shipped" into "none" behind a running script's back; * allows anything and an empty value allows nothing beyond hMailServer's own objects (IniFileSettings.cpp:389; Common/Scripting/ScriptObjectPolicy.cpp:21-23, 48); applies after a service restart) (source: ServerSettingsView.xaml.cs BuildAdvanced :2344-2440, ArchiveDomains :2390, ScriptAllowedObjects :2433-2440)
  • Event scripts (scripts): loads the file at Settings.Scripting.CurrentScriptFile (or Directory\EventHandlers.vbs) from the LOCAL disk; "Save & reload" writes a .bak, then Scripting.Reload() + CheckSyntax(); "Check syntax"; "Reload from disk"; "Insert template…" with three VBScript OnAcceptMessage starters (external AV/DLP, webhook, HTTP API verdict) (source: Views/ScriptsView.cs:13-18, 52-96, 120-295)
  • Server limits & expert settings (hardening): cards "Timeouts and queue bounds" (FinalizationTimeout 240, DNSQueryTimeout 10, ClientSessionCeiling 1800, DBConnectionAcquireTimeout 60, ScriptTimeout 60, ExternalProcessTimeout 300, AsyncQueueStallThreshold 120, AsyncQueueReservedThreads 2), "Front-end proxies (PROXY protocol and XCLIENT)" (SMTPProxyProtocolEnabled 0, SMTPProxyProtocolTrustedIPs, SMTPXClientEnabled 0, SMTPXClientTrustedIPs - empty lists trust nobody), "Received headers" (AuthUserReplacementIP, AddXAuthUserHeader 0, AddXAuthUserIP 1, AddXOriginalRcptTo 0), "Refused connections" (BlockedIPHoldSeconds 0), "Message store durability" (MessageStoreFsync 0, MessageStoreConsistencyCheck 0), "Per-account sending limits" (INI sections [SendingLimits] MaxMessagesPerAccountPerPeriod 0, MaxRecipientsPerAccountPerPeriod 0, PeriodHours 24, StateSaveIntervalSeconds 10 and [SendingLimitsOverrides] lines address=messages:recipients[:hours] - re-read within seconds, NO restart), "Submission rate limits" (MaxSubmissionsPerIPPerMinute 0), "Server-generated mail" (DaemonAddressDomain), "Low-level tuning" (SMTPDMaxSizeDrop 0, LoadHeaderReadSize 4000, LoadBodyReadSize 4000), "Stored secret protection" (ProtectStoredSecretsWithDPAPI 1 - covers exactly five secrets: DB password, relayer password, route passwords, fetch-account passwords, certificate passphrases; NOT the SRS/BATV/OAuth2/pepper/metrics/service-account secrets), "Windows service account" (ServiceAccountName - read only at service REGISTRATION, empty = leave unchanged; ServiceAccountPassword stored as typed; live SCM readout and the grants the account needs), "Settings that used to be on this page" (links) (source: FeatureSettingsView.xaml.cs:2135-2537; IniFileSettings.cpp:236, 410, 620-622)

7.9 About

  • "About": Control Panel version and .NET runtime, connected server version/host, GitHub link github.com/Progressiverobot/hmailserver, licence AGPLv3, maintainer card (Christopher Holloway / Progressive Robot Ltd, www.progressiverobot.com) (source: Views/AboutView.cs:23-176)

8. Pages and cards that exist for features that are OFF by default

Page / card Gate Default Source
Message trace page; Logging > "Message trace" card INI MessageTraceEnabled 0 IniFileSettings.cpp:626; MessageTraceView.cs:76-78
Quarantine page; Anti-spam > "Quarantine instead of refusing" INI QuarantineEnabled 0 IniFileSettings.cpp:624; IDL:2595
API & monitoring > REST API; REST API keys page (works while the listener is off) INI RestApiPort 0 IniFileSettings.cpp:591; ApiKeysView.cs:31-35
API & monitoring > Monitoring INI MetricsServerPort; Otel*Endpoint 0; empty IniFileSettings.cpp:426; FeatureSettingsView.xaml.cs Monitoring card
API & monitoring > ManageSieve INI ManageSieveServerPort 0 IniFileSettings.cpp:453
Certificates (ACME) INI AcmeEnabled 0 IniFileSettings.cpp:595
Web services & autoconfiguration (all cards) INI WebServicesHttpPort / WebServicesHttpsPort 0 / 0 IniFileSettings.cpp:602-603; card "Nothing below is served until a port is set here"
Authentication > OAuth2 INI OAuth2Enabled 0 IniFileSettings.cpp:208
Directory authentication (LDAP) INI [LDAP] Enabled 0 LdapSettings.cpp:229
Directory synchronisation > Unattended synchronisation INI [LDAP] SyncScheduleMinutes 0 LdapSettings.cpp:322
Transport security > ARC sealing, SRS, BATV, Authentication-Results, TLS-RPT, DMARC rua ArcSealingEnabled, SRSEnabled, BATVEnabled, AuthenticationResultsEnabled 0; TlsRptFromAddress, DmarcRptFromAddress empty off IniFileSettings.cpp:455, 480, 614, 616; FeatureSettingsView.xaml.cs Security cards
Anti-spam > ARC inbound filtering COM ArcFilteringEnabled + non-empty ArcTrustedSealers does nothing with an empty list IDL:2583-2586
Anti-spam > External filtering engine (rspamd) INI FilterHookUrl empty ServerSettingsView.xaml.cs BuildAntiSpam
Auto-ban > Per-name lockout INI AccountLockoutThreshold 0 (page default) ServerSettingsView.xaml.cs BuildAutoBan
Auto-ban > Logon tarpit INI LogonTarpitSeconds 0 IniFileSettings.cpp:675
Anti-spam > Recipient tarpit COM AntiSpam.TarpitCount (INI SmtpTarpitCount) 0 IniFileSettings.cpp:676
Anti-spam > SpamAssassin learns from Junk (editor new in 6.2.28; key since 6.2.25) INI SpamAssassinLearnOnMove 0 IniFileSettings.cpp:354
Delivery of e-mail > hard-linked local copies (editor new in 6.2.28; key since 6.2.25) INI DeliveryHardLinks 0 IniFileSettings.cpp:312
API & monitoring > Updates (new in 6.2.28) INI UpdateCheckEnabled 0 IniFileSettings.cpp:489; FeatureSettingsView.xaml.cs:1932
API & monitoring > Updates > forward proxy (new in 6.2.28) INI HttpProxy empty (direct) IniFileSettings.cpp:492; FeatureSettingsView.xaml.cs:1936
Server limits > PROXY protocol / XCLIENT SMTPProxyProtocolEnabled, SMTPXClientEnabled 0 IniFileSettings.cpp:620-622
Server limits > fsync / consistency check; Diagnostics consistency card MessageStoreFsync, MessageStoreConsistencyCheck 0 / 0 (page default) IniFileSettings.cpp:410; UtilityViews.cs ReadConsistencyReport
Performance > Full-text term index INI IndexerFullText 0 IniFileSettings.cpp:365
Logging > JSON lines INI JsonLogging 0 IniFileSettings.cpp:406
Backup > Scheduled backups ScheduledBackupTime / ScheduledBackupIntervalMinutes empty / 0 BackupView.xaml.cs:33-39
Advanced > Message archiving INI ArchiveDir empty ServerSettingsView.xaml.cs BuildAdvanced
Web services > CalDAV/CardDAV discovery CalDavRedirectUrl/CardDavRedirectUrl empty (404) FeatureSettingsView.xaml.cs WebServices

On by default (for contrast): DaneEnforcementEnabled, DnssecValidationEnabled, MtaStsEnabled, MtaStsHostingEnabled, AutoconfigEnabled, WindowsEventLogEnabled, ProtectStoredSecretsWithDPAPI, TlsSessionTicketsEnabled, UseDNSCache, DNSBLChecksAfterMailFrom, RejectFullMailboxAtRcpt, GreylistingEnabledDuringRecordExpiration all default 1, and IMAPCompressionEnabled, OutboundPipelining, OutboundChunking and UpdateBackupBeforeApply (meaningful only once the update check is on) - of those four only IMAPCompressionEnabled and UpdateBackupBeforeApply are new keys in 6.2.28, OutboundPipelining and OutboundChunking having shipped in 6.2.25 with no editor until now (source: IniFileSettings.cpp:161, 236, 249, 400-404, 497, 509, 584, 607, 611, 628, 641, 732-733)

9. Contradictions and stale claims between documents and code

  • hmailserver documentation/hmailserver-documentation.md §5 (line 328) and §19 (line 1210) and settings.md line 3 say the Control Panel is a .NET 8 app / needs the .NET 8 Desktop Runtime. Code: net10.0-windows (ControlPanel.csproj:5); About page says .NET 10 (AboutView.cs:85); README.md:111 and :130 say .NET 10.
  • Documentation §5.3 (lines 353-367) and §20 (lines 1322-1362) describe the OLD tree: groups Status / Domains / Rules / Settings (with Anti-spam ▸ Anti-virus ▸ Logging ▸ Security ▸ Network ▸ Maintenance) / Utilities, and pages "Settings → Auto-ban & SSL/TLS" and "Settings → Advanced hardening". Code: eight task-based groups (section 5 above); "Auto-ban & SSL/TLS" is split into "Auto-ban" and "SSL/TLS"; "Advanced hardening" is now titled "Server limits & expert settings" (NavigationMap.cs BuildRoots and comments). The doc's statement (line 1207) that the tree "deliberately mirrors the old Administrator's layout" is what NavigationMap.cs explicitly replaced.
  • Documentation §20 lists 38 pages; code registers 55. Absent from the doc: Diagnosing stalled mail, Message trace, External setup, DNS records, REST API keys, Spam filtering overview, Quarantine, Blocked senders, Virus scanning overview, Transport encryption overview, Authentication, Directory authentication (LDAP), Directory synchronisation, Administrative access, DNS resolver, Web services & autoconfiguration (the doc folds web services into "API & monitoring") (source: MainWindow.xaml.cs RegisterPages :114-171 vs documentation lines 1324-1362).
  • Documentation §17.4 (line 1094) locates admin two-factor at "Settings → ... → Two-factor authentication". Code: Access & abuse protection > Administrative access > "Two-factor" tab, plus the link on the Connect screen (ServerSettingsView.xaml.cs BuildAdminAccess; ConnectView.xaml:54).
  • Documentation §21 (line 1401) and README.md:300 say the Control Panel "offers to restart the service when a change requires it". Code: only the INI-backed FeatureSettingsView pages (Transport security, ACME, API & monitoring, Server limits, Authentication, DNS resolver, Web services) prompt "Restart it now?" (FeatureSettingsView.xaml.cs:2966); ServerSettingsView pages that contain INI rows only show a toast "INI settings need a service restart" without offering one (ServerSettingsView.xaml.cs:3376); the LDAP pages and API keys need no restart; Backup's schedule card says "apply after a service restart" with no prompt.
  • README.md:300 refers to "the Control Panel under Settings" - there is no "Settings" group any more.
  • settings.md (dated 2026-06-15, server 6.2.5) says FeatureSettingsView spans "four nav pages" and that "All 104 INI keys" are covered; code has seven FeatureSettingsView sections (enum at FeatureSettingsView.xaml.cs:35-44) and the generated settings index holds 389 entries, 367 of them from those two views. Its Section A table lists "Auto-ban & SSL/TLS" as one page and files "admin password" under "Advanced & scripting"; both have moved (AdminAccess section, ServerSettingsView.xaml.cs:2568).
  • settings.md Section A says ServerSettingsView pages "mirror the classic Administrator" - superseded by the task-based map.
  • Documentation §5.2 and Views/WelcomeView.cs:11 ("Landing page shown after connecting") imply Welcome is the landing page; code navigates to the Dashboard after connecting (MainWindow.xaml.cs OnConnected: NavigateTo("dashboard")).
  • Documentation §20 "Dashboard: Live graphs — throughput, sessions, service state": the dashboard has no service-state readout (that is on Server status); it has five KPIs, two charts and the external-setup summary (DashboardView.xaml).
  • Documentation §20 "Incoming relays: Trusted forwarders whose Received headers are believed" vs code subtitle "whose IP addresses should not count as the connecting client in anti-spam host checks" (UtilityViews.cs:33).
  • Documentation §20 "Blocked attachments: Refuse attachments by file extension" - code strips matching wildcards and only when AntiVirus.EnableAttachmentBlocking is on (CollectionSpecs.cs:118).
  • NavigationMap.cs class summary (:91) says "Of 42 pages, 30 sat under a single Settings group" - historical; 55 pages are registered today (internal comment, not user-facing).
  • The "Web Control Deck at the listener root" named on the API & monitoring card is real (confirmed 2026-09-08): the REST listener serves GET / and /index.html from {app}\WebAdmin\index.html without authentication (hmailserver/source/Server/Common/Util/RestApiServer.cpp:1275-1277, HandleWebAdminPage_ :2977-2980; installed by hmailserver/installation/section_files_64.iss:13), a single-page browser UI titled "hMailServer — Control Deck" that reads and writes through /api/v1. Four views - Dashboard, Domains (with accounts), Delivery queue and DANE/TLSA - have been there since it was written; new in 6.2.28 are four read-only ones: Settings (the server-wide keys as a table), Logs (the file list, and any file's last 200 or 2,000 lines), Certificates (each by name and file, never a key password, which the route does not carry either) and Rules (the global rules with their criteria and actions, in evaluation order). Nothing on those four changes the server and a read-only key sees them all; writing settings, certificates and rules is still Control Panel and COM only. The page is served as the bytes on disk, so its own glyphs survive, under a Content-Security-Policy that allows nothing but its own inline script and style, X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer. Also new in 6.2.28: the same listener serves the self-service portal at /portal (RestApiServer.cpp:1281), which is for mailbox owners, not administrators.
  • Totp.cs header comment (line 5-7) says the secret is stored "exactly like hMailServer Administrator" - consistent with documentation line 1209 ("TOTP secret carries over"); no contradiction, recorded as confirmed.

10. Unconfirmed / not verified here

  • Server-side defaults for AccountLockoutThreshold/WindowMinutes/Minutes, MessageStoreConsistencyCheck, ScheduledBackup*, AVFail*, FilterHook*, IMAPSearch*, Pop3LoginDelaySeconds, TlsSession*, PasswordPolicy*, Outbound/FetchOAuth2* were taken from the Control Panel's Default = values, not re-grepped in IniFileSettings.cpp.
  • The exact contents of the Account dialog "Sieve", "Folders" and "Directory" tabs beyond the COM properties they write (SieveScript, ADDomain, ADUsername) were not read line by line.
  • Whether the External setup "Cannot tell" logic and the DNS-records checks behave correctly on a remote session was not exercised; the code states the intent (ExternalSetupView.cs:42-49; DnsRecordsView.cs:326-327).
  • README.md:146 says the Control Panel does not need the admintools component; consistent with late binding in ServerSession.cs but not tested on a machine without the type library.

Clone this wiki locally