-
Notifications
You must be signed in to change notification settings - Fork 3
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.
- Assembly name
hMailCP(hMailCP.exe), targetnet10.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
ProtectedDataclass, not a package (source: ControlPanel.csproj:52-56; Services/Totp.cs:223) - Solution
Tools/ControlPanel.slnholds 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, componentcontrolpanel(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 assvorpt-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 intoResources/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 bybuild/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.pychecks that server-side literals, page titles and numbers survive translation;build/check-mnemonics.pychecks 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)
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
| 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).
- 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-filledAdministrator), 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.CheckAsyncprobes 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; thenAuthenticate(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.Applicationre-verifies the link at most once per 2 s by readingServerState(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 localhMailServerWindows 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)
-
DescribeComErrormaps: 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.StatusProcessedMessages, 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/ComWriteSettingtoSettings.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)
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
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
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).
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
Three details in that machine are load-bearing:
- The probe asks for
ServerState, notVersion. A shutting-down service keeps answeringVersionfrom a static string long after it has closed its database, so aVersionprobe 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 |
- Secret stored under
HKLM\SOFTWARE\hMailServer, valueAdminTotpSecret, 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=30and 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 COMSettings.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 setsSecondFactorRequired), COM callers useAuthenticateWithCode, the REST API uses anX-hMailServer-OTPheader. The old HKLMAdminTotpSecretcard 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)
| 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 |
-
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 andnavgroup-<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 bybuild/generate-settings-index.ps1- 347 at 6.2.24 (source: PaletteSearch.cs class summary; IntentIndex.cs:67-227; Services/SettingsSearchIndex.g.cs header andgrep -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 throughViews/Mnemonic.cs, which underlines the letter, registers the key withAccessKeyManagerfor the editor, answers the manager's AccessKeyPressed question for it (a bare text box does not and is skipped) and setsAutomationProperties.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 bybuild/check-mnemonics.pyin the style workflow.MnemonicText(Services, tested) is the one parser and escape: data-driven captions are escaped with a doubled underscore.build/check-mnemonics.pyalso 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.HeadingLevelvia thePageTitlestyle); 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")
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
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.
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" |
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 > GroupsSettings > Maintenance > Groups
|
Groups (groups) |
Settings > Delivery of e-mail |
Delivery of e-mail (delivery) |
Settings > Advanced > Routes |
Routes (routes) |
Settings > Advanced > Incoming relaysSettings > Network > Incoming relays
|
Incoming relays (relays) |
Settings > Advanced > Server messagesSettings > 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 > ProtocolsSettings > Protocols > SMTPSettings > Protocols > POP3Settings > Protocols > IMAP
|
Protocols (protocols) |
Settings > Advanced > TCP/IP portsSettings > 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 certificatesSettings > Security > SSL certificates
|
SSL certificates (certs) |
Settings > Security > Certificates (ACME) |
Certificates (ACME) (acme) |
Settings > Security > SSL/TLSSettings > 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/TLSSettings > Security > Auto-banSettings > Advanced > Auto-ban
|
Auto-ban (autoban) |
Settings > Advanced > IP rangesSettings > Security > IP ranges
|
IP ranges (ipranges) |
Utilities > BackupUtilities > Backup & restore
|
Backup & restore (backup) |
Settings > Maintenance > Performance |
Performance (performance) |
Settings > AdvancedSettings > Maintenance > Advanced & scripting
|
Advanced (advanced) |
Settings > Advanced > ScriptsSettings > Maintenance > Event scripts
|
Event scripts (scripts) |
Settings > Advanced hardeningSettings > 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.
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)
- Two data-driven settings views:
ServerSettingsViewedits the COMapp.Settingstree via dotted paths ("AntiSpam.SpamMarkThreshold") resolved by reflection over IDispatch;FeatureSettingsVieweditshMailServer.INI[Settings]keys throughIniFeatureStore(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
hMailServervia ServiceController (60 s waits) (source: FeatureSettingsView.xaml.cs:2948-3040) -
IniFeatureStorelocates the INI fromHKLM\SOFTWARE\hMailServer\InstallLocation(Bin\hMailServer.INI, thenhMailServer.INI) in the 64- then 32-bit registry view, else from thehMailServerservice's PathName via WMI (hMailServer.inibeside the exe);IsAvailable= file found locally (source: Services/IniFeatureStore.cs:61-120) - Read/Write of
[Settings]uses the local file when available, otherwise COMGetIniSetting/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
SecretSettingeditor 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)
-
SettingClaimspins wording for settings the server only partly honours:WorkerThreadPriorityis INERT (shown read-only, "the server stores this value and never reads it");UserInterfaceLanguageonly reaches third-party COM tools;Cache.*MaxSizeKbapply immediately but reset to 10240 KB at every restart;JsonLoggingis overridden whileLogging.LogFormatis NCSA;Logging.LogFormatNCSA = NCSA Common Log Format (not combined, not AWStats);Logging.DeviceSQL falls back to files if the database is unreachable;OtelEndpoint= traces only,OtelMetricsEndpoint,OtelLogsEndpointseparate (source: Services/SettingClaims.cs:96-236)
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."]
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).
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.
| 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()).
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) |
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.
- 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 loadUtilities.GetMetricHistoryfor 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 sameExternalSetupChecksas 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_())
- 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, fromStatus.UpdateState/AvailableVersion/AvailableVersionPublished/UpdateLastChecked/UpdateLastError, prefixed by the last apply's outcome fromStatus.UpdateApplyOutcome), Database (Type viaDatabase.DatabaseType1 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);DenyMailFromNullon (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 fromStatus.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 unlessMessageTraceEnabledis set - off by default (source: Views/MessageTraceView.cs:76-78, 152-234; IDL:2362 GlobalObjects.MessageTrace) - Live logs (
logs): tails the newesthmailserver_*.login[Directories] LogFolderread 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" callsApplication.Diagnosticswith LocalDomainName (pre-filled with the first hosted domain) and TestDomainName (defaultgmail.com) thenPerformTests()and prints [ OK ]/[FAIL] per test; a "Message-store consistency" card reads the local recovery report (MessageStoreConsistencyReport.FileNamein the log folder) written by the server'sMessageStoreConsistencyChecktask (start-up + hourly), with "Refresh"/"Open report"; on a remote session it says the number is also published as metrichmailserver_messagestore_missing_files(source: Views/UtilityViews.cs DiagnosticsView ~436-851; IDL:1676 Diagnostics) - MX query (
mxquery): callsApplication.Utilities.ResolveMXRecords(domain)- the server's own resolver, honouringDNSServerin hMailServer.INI (issue #29 replaced annslookupshell-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 (fromSettings.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 INIMtaStsHostingEnableddefault 1,WebServicesHttpsPort,MtaStsPolicyModedefault enforce,MtaStsPolicyMaxAgedefault 604800,MtaStsPolicyMx) and TLS-RPT (usesTlsRptFromAddress), 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" (COMLogging.Enabled,LogApplication,LogSMTP,LogIMAP,LogPOP3,LogTCPIP,LogDebug,AWStatsEnabled,KeepFilesOpen,Logging.Devicecombo "Files on disk"/"Database (SQL)",Logging.LogFormatcombo "hMailServer (tab separated)"/"NCSA Common Log Format", INIJsonLoggingdefault 0), "Log detail" (INILogLeveldefault 9,MaxLogLineLendefault 500,SepSvcLogsdefault 0), "Log retention" (INILogDeleteDaysdefault 0), "Message trace" (INIMessageTraceEnableddefault off,MessageTraceRetentionDaysdefault 30), "Log files" (shows[Directories] LogFolderand "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;RestApiPortdefault 0 = disabled, placeholder 8045;RestApiBindAddressdefault 127.0.0.1;RestApiCertificateFile,RestApiPrivateKeyFileoptional, falls back to the ACME certificate; "TLS is required unless the listener is bound to 127.0.0.1"), "Monitoring" (MetricsServerPortdefault 0,MetricsHistoryDaysdefault 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 -MetricsServerBindAddressdefault 127.0.0.1,MetricsServerAuthToken,MetricsServerAuthUsername,MetricsServerAuthPassword,MetricsServerCertificateFile,MetricsServerPrivateKeyFile,OtelEndpoint,OtelMetricsEndpoint,OtelLogsEndpoint,OtelMetricsIntervaldefault 60,OtelServiceNamedefault hmailserver,SlowQueryLogMillisecondsdefault 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:UpdateCheckEnableddefault 0 - a daily read of the release feed, off until turned on, sends nothing but the request;UpdateCheckHours24 (1-168);UpdateChannelstable/prerelease;UpdateFeedUrlempty = this project's GitHub releases, set for a mirror;HttpProxyempty = a direct connection, otherwisehost:portor[ipv6]:portfor 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;UpdateAutoDownload0;UpdateWindowempty = 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;UpdateBackupBeforeApply1 - no destination or a failed backup means no apply;UpdateRequireAuthenticode0 - leave off, this project's releases are not Authenticode-signed so 1 refuses every release, the Sigstore check is not optional;UpdateServiceWaitSeconds180; and five private-Sigstore settingsUpdateSourceRepository,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" (WindowsEventLogEnableddefault 1,WindowsEventLogLeveldefault 2 with options 1-4), "ManageSieve (RFC 5804)" (ManageSieveServerPortdefault 0, placeholder 4190;ManageSieveServerBindAddressdefault 127.0.0.1; live STARTTLS-availability state), "Operability" (ShutdownDrainSecondsdefault 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/revokeshMailServerApiKeys.inibeside hMailServer.INI (one[Key.<id>]section with Label, Expires "yyyy-MM-dd HH:mm:ss", AllowedFrom, Scope, Domains, Hash written LAST); token formathmapi_+ 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)
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 |
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 |
| 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).
- Domains (
domains): on-screen "Domains & accounts"; left list of domains (search, "Properties", "Add domain" with boxnewdomain.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 viaApplication.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; writesActive,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,
ServerTypecombo 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 wasConnectionSecurity == CSSSLand 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), andMirrorFolders"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.AppPasswordscollection; 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): listsSettings.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.Groupslist 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)
- Delivery of e-mail (
delivery): tabs Delivery (COMSMTPNoOfTries,SMTPMinutesBetweenTry,MaxNumberOfMXHosts, INIMXTriesFactordefault 0, COMSMTPDeliveryBindToIP,SMTPConnectionSecuritycombo,AddDeliveredToHeader, INIQuickRetries0,QuickRetriesMinutes6,QueueRandomnessMinutes0,MaxOutboundPerDestinationPerMinute0, and - editors new in 6.2.28 for three keys that shipped in 6.2.25 - INIOutboundPipeliningdefault on (send a transaction's commands without waiting for each answer, only to a server that advertises PIPELINING),OutboundChunkingdefault on (BDAT instead of dot-stuffed DATA; also what relays a BINARYMIME message onward unchanged),DeliveryHardLinksdefault 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)":SMTPRelayerwithhost1|host2failover,SMTPRelayerPort,SMTPRelayerConnectionSecurity,SMTPRelayerRequiresAuthentication,SMTPRelayerUsername, password viaSetSMTPRelayerPassword; card "OAuth2 for the relay (Microsoft 365)": INIOutboundOAuth2Hostsdefaultsmtp.office365.com,OutboundOAuth2TokenUrl,OutboundOAuth2ClientId,OutboundOAuth2ClientSecret,OutboundOAuth2Scope,OutboundOAuth2FixedToken,FetchOAuth2Hostsdefaultoutlook.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 fromSettings.Routes; inline add (domain, host, port default 25; NumberOfTries 3, MinutesBetweenTry 10), "Edit" opens "Route - {domain}" with tabs General, Delivery, Addresses, Security, Authentication writingTargetSMTPHost/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.IncomingRelaysName/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.ServerMessagesName/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)
- Spam filtering overview (
spamoverview): read-only; readsSettings.AntiSpamthresholds, headers, HELO/PTR/MX checks and scores, SPF/DKIM/DMARC and scores, SpamAssassin, greylisting and bypasses, blocked-sender count; judgement inServices/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(), windowQuarantineRetentionDays), "Refresh"; empty state text says quarantining is off unlessQuarantineEnabledis 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": INIQuarantineEnabledoff by default,QuarantineRetentionDaysdefault 30; "Recipient tarpit": COMAntiSpam.TarpitCount- recipients a message may name before the delay starts, 0 = off - andAntiSpam.TarpitDelay- seconds each further RCPT TO in an unauthenticated session waits, at most 30; stored as INISmtpTarpitCount/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, INISpfVoidLookupLimit,DKIMVerificationEnabled+DKIMVerificationFailureScore, INIDkimAcceptSha1off,DMARCEnabled+DMARCFailureScore, INIDmarcTreeWalkEnabled, INIDmarcRptSchemaVersiondefault 1, COMArcFilteringEnabled+ArcTrustedSealers- "does nothing at all" with an empty sealer list), Host checks (CheckHostInHelo(+Score),CheckPTR(+Score),UseMXChecks(+Score)), Greylisting (GreyListingEnabled,GreyListingInitialDelay,GreyListingInitialDeleteandFinalDeleteshown in days (stored hours, Divisor 24),BypassGreylistingOnMailFromMX,BypassGreylistingOnSPFSuccess, INIGreylistingEnabledDuringRecordExpirationdefault 1,GreylistingRecordExpirationIntervaldefault 240, button "Clear greylisting triplets"), SpamAssassin (SpamAssassinEnabled/Host/Port/MergeScore/Score, "Test SpamAssassin connection", INISAMinTimeout30,SAMaxTimeout90,SAMoveVsCopyoff,SpamAssassinUserempty andSpamAssassinUserFromRecipientoff - the spamdUser:header, applied on Reinitialize (added 5 September 2026); the editor new in 6.2.28 for a key that shipped in 6.2.25: INISpamAssassinLearnOnMovedefault 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)": INIFilterHookUrl(empty = none),FilterHookTimeoutSeconds10,FilterHookFailClosedoff,FilterHookRejectScore100,FilterHookMaxMessageSizeKB10240) (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.SURBLServersActive/DNSHost/RejectMessage/Score (default 5) (source: CollectionSpecs.cs:38-51) - DNS blacklists (
dnsbl):AntiSpam.DNSBlackListsActive/DNSHost/ExpectedResult/RejectMessage/Score (default 5) (source: CollectionSpecs.cs:53-67) - White list (
spamwhitelist): on-screen "Anti-spam white list";AntiSpam.WhiteListAddressesLowerIPAddress/UpperIPAddress/EmailAddress/Description (source: CollectionSpecs.cs:69-82) - Blocked senders (
blockedsenders):AntiSpam.BlockedSendersAddress (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.GreyListingWhiteAddressesIPAddress/Description (source: CollectionSpecs.cs:102-113) - Virus scanning overview (
virusoverview): read-only; reads ClamAV/ClamWin/custom scanner settings; exists becauseVirusScanner::ScanFile_treats "every scanner errored" as NoVirusFound, so an enabled-but-broken scanner looks like a clean scan; judgement inServices/VirusPipeline.cs(source: Views/VirusOverviewView.cs:21-44, 131-139) - Anti-virus settings (
antivirus): on-screen "Anti-virus"; tabs General (AntiVirus.Actioncombo,NotifySender,NotifyReceiver,MaximumMessageSize,EnableAttachmentBlocking; card "When a scanner cannot run": INIAVFailAction0 deliver / 1 hold (default 0),AVFailRetryMinutes15,AVFailMaxHolds16), ClamAV (ClamAVEnabled/Host/Port, "Test ClamAV connection", INIClamMinTimeout15,ClamMaxTimeout90), 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(expectsPONG) andnVERSIONbefore 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.BlockedAttachmentsWildcard (default*.exe)/Description; requiresAntiVirus.EnableAttachmentBlockingon the Anti-virus page (source: CollectionSpecs.cs:115-126)
- Protocols (
protocols): tabs Services (COMServiceSMTP,ServiceIMAP,ServicePOP3), SMTP (HostName,MaxSMTPConnections,MaxMessageSize,MaxSMTPRecipientsInBatch,WelcomeSMTP,AllowSMTPAuthPlain,DenyMailFromNull, INIRejectFullMailboxAtRcptdefault on, INIQuotaWarningPercentdefault 90, INIArchiveRetentionDaysdefault 0, INIMetricsPerDomainEnableddefault off,AllowIncorrectLineEndings,DisconnectInvalidClients,MaxNumberOfInvalidCommands, INISmtpAuthenticatedSenderCheckdefault 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,CreateDefaultSpecialUseFoldersEnableddefault 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: INIIMAPCompressionEnableddefault 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": INIIMAPSearchTimeout60,IMAPSearchMaxMegabytes2048; card "Change history for synchronising clients": INIIMAPExpungeRetentionRecords5000; card "Repair": button "Recalculate folder UID counters"), POP3 (MaxPOP3Connections,WelcomePOP3; card "How often a client may check for mail": INIPop3LoginDelaySeconds0), Timeouts (INISMTPDMinTimeout10,SMTPDMaxTimeout1800,SMTPCMinTimeout30,SMTPCMaxTimeout600,POP3DMinTimeout10,POP3DMaxTimeout600,POP3CMinTimeout30,POP3CMaxTimeout900) (source: ServerSettingsView.xaml.cs BuildProtocols :1038-1217, SmtpAuthenticatedSenderCheck :1067, IMAPCompressionEnabled :1109-1116; IniFileSettings.cpp:497, 641, 643, 656) - TCP/IP ports (
ports): grid ofSettings.TCPIPPortsProtocol (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 (writesProtocol,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" (WebServicesHttpPortdefault 0,WebServicesHttpsPortdefault 0 - "Nothing below is served until a port is set here",WebServicesBindAddressdefault 0.0.0.0,WebServicesCertificateFile/WebServicesPrivateKeyFileoptional, fall back to ACME), "Client autoconfiguration (autoconfig & autodiscover)" (AutoconfigEnableddefault 1,AutoconfigClientHost), "MTA-STS policy hosting" (MtaStsHostingEnableddefault 1,MtaStsPolicyModedefault enforce,MtaStsPolicyMaxAgedefault 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" (UseDNSCachedefault 1 - toggles DNS_QUERY_BYPASS_CACHE, i.e. Windows' cache, ignored when DNSServer is set), "DNS blacklist checks" (DNSBLChecksAfterMailFromdefault 1) (source: FeatureSettingsView.xaml.cs:2624-2666; IniFileSettings.cpp:249, 400)
- SSL certificates (
certs): grid ofSettings.SSLCertificatesName, Certificate file, Private key file, Certificate state (parses, expiry, days remaining), Private key state (exists, encrypted, matches the certificate) viaCertificateInspectoroff the UI thread - honest "cannot be checked from here" on a remote session; "Private key passphrase" card (COMPrivateKeyPassword, 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)":AcmeEnableddefault 0,AcmeContactEmail,AcmeDomains,AcmeDirectoryUrldefaulthttps://acme-v02.api.letsencrypt.org/directory,AcmeHttpPortdefault 80,AcmeCertificateDirectory(empty = Data\ACME),AcmeReuseKeydefault 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; readsTlsVersion10..13Enabled,SslCipherList,VerifyRemoteSslCertificate, the ports and certificates; judgement inServices/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 ontlsoverview) - SSL/TLS (
tls): tabs Protocol versions (COMTlsVersion10Enabled,TlsVersion11Enabled,TlsVersion12Enabled,TlsVersion13Enabled), Ciphers (SslCipherList- "TLS 1.2 and below (OpenSSL format)", the single valueAEAD-ONLYis a named preset; INITlsCipherSuites13for 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: INITlsKeyExchangeGroups- 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 fallbacksecp384r1:x25519:secp256r1used instead; applies after a service restart), Session resumption (INITlsSessionTicketsEnableddefault 1,TlsSessionCacheSizedefault 0 (-1 = no cache),TlsSessionTimeoutSecondsdefault 0,TlsTicketKeyRotationSecondsdefault 0) (source: ServerSettingsView.xaml.cs BuildTls :1719-1843, TlsKeyExchangeGroups :1736-1743; IniFileSettings.cpp:584, 687) - Transport security (
security): cards "DANE & DNSSEC" (DaneEnforcementEnableddefault 1,DnssecValidationEnableddefault 1,DnssecTrustAnchors), "MTA-STS" (MtaStsEnableddefault 1 - honouring OTHER domains' policies), "ARC sealing" (ArcSealingEnableddefault 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" (DKIMSignatureValiditySeconds0,DKIMEnforceSignatureExpiry1,DKIMExpiryClockSkewSeconds300), "DKIM oversigning" (DkimOversignHeadersempty = off), "Authentication results on inbound mail" (AuthenticationResultsEnabled0,ReceivedSpfHeaderEnabled0,AuthenticationResultsIdentity), "TLS reporting (TLS-RPT)" (TlsRptFromAddressempty = disabled,TlsRptOrganizationNamedefault hMailServer), "DMARC aggregate reporting (rua)" (DmarcRptFromAddressempty = disabled,DmarcRptOrganizationName), "Forwarded mail & bounce protection (SRS / BATV)" (RewriteEnvelopeFromWhenForwarding0,SRSEnabled0 +SRSSecret,BATVEnabled0 +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)
- Authentication (
authentication): cards "OAuth2 / external identity provider" (OAuth2Enableddefault 0,OAuth2RequireTLS1,OAuth2Issuer,OAuth2Audience,OAuth2AllowedAlgorithmsdefault RS256,OAuth2UsernameClaimdefault email,OAuth2PublicKeyFile,OAuth2HmacSecret, and since 5 September 2026OAuth2JwksUrl,OAuth2JwksCacheSeconds3600,OAuth2IntrospectionUrl,OAuth2IntrospectionClientId,OAuth2IntrospectionClientSecret,OAuth2IntrospectionCacheSeconds300,OAuth2IntrospectionFailOpenoff; live coherence warning - the mailbox must still exist locally), "Password storage" (PreferredHashAlgorithmdefault 4 with options 5 Argon2id / 4 PBKDF2 / 3 SHA-256 / 2 MD5 / 1 Blowfish;MinimumAcceptedHashAlgorithmdefault 0 with 0/3/4/5;PasswordPepper- changing it invalidates ALL existing passwords; INIPasswordHashIterations0,PasswordHashMemoryKB0,PasswordHashTimeCost0 - 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); keysEnabled(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 whoseADDomainNameis set; INI[LDAP]keysSyncFilter,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" viaSetAdministratorPassword- used by the Control Panel, the REST API and COM scripts, stored hashed in hMailServer.ini; "Policy for mailbox passwords": INIPasswordPolicyMinimumLength0,PasswordPolicyRequireMixedCase,PasswordPolicyRequireDigit,PasswordPolicyRequireNonAlphanumeric,PasswordPolicyRejectCommon- all off, applied when a password is CHOSEN, never at logon; "Reuse and expiry":PasswordPolicyHistoryCount0,PasswordPolicyMaximumAgeDays0 - 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" (COMAutoBanOnLogonFailure,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" (INIAccountLockoutThreshold0 = off,AccountLockoutWindowMinutes30,AccountLockoutMinutes30; counted per user name across SMTP AUTH, POP3, IMAP, ManageSieve, REST), card "Logon tarpit" (INILogonTarpitSeconds0 = 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 ofSettings.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)
- Backup & restore (
backup): COMSettings.BackupDestination, BackupDomains, BackupMessages, BackupSettings, CompressDestinationFiles (setters persist immediately - no Save method); INIBackupMessagesDBOnly(local only); checkbox "Verify each backup by extracting its message store and reconciling it against the rows" = INIBackupVerifyRestore(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: pickHMBackup*.xmlon 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 newestHMBackup YYYY-MM-DD HHMMSS.7zarchive 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 (COMMaxDeliveryThreads,MaxAsynchronousThreads,TCPIPThreads, INIMaxNumberOfExternalFetchThreads15,WorkerThreadPriorityshown 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 fromCache.*HitRate), Indexing (MessageIndexing.Enabled, INIIndexerFullMinutes720,IndexerFullLimit25000,IndexerQuickLimit1000,IndexerFullText0 - needs indexing on,IndexerFullTextBatchSize250,IndexerFullTextMinTokenLength3,IndexerFullTextMaxTokensPerMessage2048; buttons "Index now, and show how far behind it is", "Discard the index and rebuild it"), Database (LOCAL INI[Database]NumberOfConnections5 - forced to 1 on MSSQLCE and labelled so,ConnectionAttempts6,ConnectionAttemptsDelay5;[Settings]DatabaseStatementTimeout30) (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" INIArchiveDir(empty = off),ArchiveHardLinks0,ArchiveDomains"Only archive mail for these domains (comma-separated; empty = every message)" (added 5 September 2026, v6.2.25); "Disk space" INIDiskSpaceWarningThresholdMB1024,MinimumFreeDiskSpaceMB100), Scripting (Scripting.Enabled,Scripting.LanguageVBScript/JScript; engine reloads on Save; the editor new in 6.2.28 for a key that shipped in 6.2.25: INIScriptAllowedObjects- 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 atSettings.Scripting.CurrentScriptFile(orDirectory\EventHandlers.vbs) from the LOCAL disk; "Save & reload" writes a.bak, thenScripting.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" (FinalizationTimeout240,DNSQueryTimeout10,ClientSessionCeiling1800,DBConnectionAcquireTimeout60,ScriptTimeout60,ExternalProcessTimeout300,AsyncQueueStallThreshold120,AsyncQueueReservedThreads2), "Front-end proxies (PROXY protocol and XCLIENT)" (SMTPProxyProtocolEnabled0,SMTPProxyProtocolTrustedIPs,SMTPXClientEnabled0,SMTPXClientTrustedIPs- empty lists trust nobody), "Received headers" (AuthUserReplacementIP,AddXAuthUserHeader0,AddXAuthUserIP1,AddXOriginalRcptTo0), "Refused connections" (BlockedIPHoldSeconds0), "Message store durability" (MessageStoreFsync0,MessageStoreConsistencyCheck0), "Per-account sending limits" (INI sections[SendingLimits]MaxMessagesPerAccountPerPeriod0,MaxRecipientsPerAccountPerPeriod0,PeriodHours24,StateSaveIntervalSeconds10 and[SendingLimitsOverrides]linesaddress=messages:recipients[:hours]- re-read within seconds, NO restart), "Submission rate limits" (MaxSubmissionsPerIPPerMinute0), "Server-generated mail" (DaemonAddressDomain), "Low-level tuning" (SMTPDMaxSizeDrop0,LoadHeaderReadSize4000,LoadBodyReadSize4000), "Stored secret protection" (ProtectStoredSecretsWithDPAPI1 - 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;ServiceAccountPasswordstored 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)
- "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)
| 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)
-
hmailserver documentation/hmailserver-documentation.md§5 (line 328) and §19 (line 1210) andsettings.mdline 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.mdSection 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.EnableAttachmentBlockingis 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.htmlfrom{app}\WebAdmin\index.htmlwithout 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: nosniffandReferrer-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.
- Server-side defaults for
AccountLockoutThreshold/WindowMinutes/Minutes,MessageStoreConsistencyCheck,ScheduledBackup*,AVFail*,FilterHook*,IMAPSearch*,Pop3LoginDelaySeconds,TlsSession*,PasswordPolicy*,Outbound/FetchOAuth2*were taken from the Control Panel'sDefault =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
admintoolscomponent; consistent with late binding in ServerSession.cs but not tested on a machine without the type library.
hMailServer 6.3.2 · AGPL-3.0-or-later · Repository · Report a documentation error
Hmail Server — full index
Start here
1. Install and run
- Before You Install
- Installing hMailServer
- Installing on Linux
- Running in a Container
- The Control Panel
- Your First Domain and Mailbox
- Connecting a Mail Client
- DNS for Your Domain
2. Secure it
3. Operate it
- Monitoring and Health
- Backup and Restore
- Troubleshooting
- Diagnosing Stalled Mail
- Relocating an Installation
- Upgrading hMailServer
- Upgrading Guide
- Migrating the Database Backend
- High Availability Runbook
- Warm Standby
- Runbooks Digest
4. Extend it
- Rules and Sieve
- Aliases Lists and Public Folders
- Routes and Relays
- The COM API and Scripting
- The REST API
- APIs Reference
5. Contribute to it
- Project Handbook
- Architecture
- Contributing
- Release Process
- Governance
- Assurance Case
- Regression Test Environment
- Fuzzing
- Regulatory Scope
- Third-Party Binaries
Look it up — from any journey