Skip to content

Control Panel Reference

chrisholloway5 edited this page Sep 4, 2026 · 28 revisions

Control Panel Reference

How hMailCP connects, its navigation map, and every page and dialog with the setting behind each field, checked against the Control Panel's source. File and line references point into the repository at the commit this page was written from (6.2.24, master of 4 September 2026). Sections headed Unconfirmed or Contradictions record what could not be verified or where documents disagreed, and are left in on purpose.

All paths below are relative to hmailserver/source/Tools/ControlPanel/ unless stated. "IDL" = hmailserver/source/Server/hMailServer/hMailServer.idl. "IniFileSettings.cpp" = hmailserver/source/Server/Common/Application/IniFileSettings.cpp. Line numbers were read on 2026-09-04 at commit 4d5da3f (branch server-fixes-wave) and are exact where given as :N, approximate where given as ~N.

1. What it is

  • Assembly name hMailCP (hMailCP.exe), target net10.0-windows, WPF, x64 only, version 6.2.24, product "hMailServer Control Panel", company Progressive Robot Ltd (source: ControlPanel.csproj:5-14, 17, 43-44)
  • 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, System.Security.Cryptography.ProtectedData, System.ServiceProcess.ServiceController (all 10.0.11) (source: ControlPanel.csproj ItemGroup)
  • Solution Tools/ControlPanel.sln holds three projects: ControlPanel (WPF), ControlPanel.Core (WPF-free logic: navigation map, palette search, setting claims, pipelines), ControlPanel.Tests (source: ls Tools/; NavigationMap.cs class summary "the tests reference ControlPanel.Core, which has no WPF")
  • Test files exist for: AccessibleNames, ApiKeyStore, ChartDataTable, ChartPalette, DirectorySyncReport, HostReachability, ListenerProbe, MessageStoreConsistencyReport, NavigationMap, NumericField, PageSplit, PaletteSearch, PaletteUsage, PasswordGenerator, PasswordStrength, SearchTerms, SettingClaims, SettingsSearchIndex, SpamPipeline, StatusSemantics, TlsPosture, VirusPipeline, WindowsServiceInfo (source: ls Tools/ControlPanel.Tests/Services)
  • Installer: Start Menu shortcut "hMailServer Control Panel" -> {app}\ControlPanel\hMailCP.exe, component controlpanel (source: hmailserver/installation/section_icons.iss:3); post-install checkbox "Run hMailServer Control Panel" (source: section_run.iss:9)
  • The Control Panel does NOT need the admintools (COM type library) component - it binds late through IDispatch (source: README.md:146; ServerSession.cs:11-14 "late binding (IDispatch), so no interop assembly is required")
  • Main window title "hMailServer Control Panel", default 1280x800, minimum 760x520, Mica backdrop, content extends into title bar (source: MainWindow.xaml:9-15)
  • Command line: hMailCP.exe /connect <host> <user> <password> connects automatically on load (source: MainWindow.xaml.cs constructor ~88-101, comment "Optional auto-connect")
  • Per-user state lives in HKCU\Software\hMailServer\ControlPanel: values WindowWidth, WindowHeight, WindowLeft, WindowTop, WindowMaximized ("1"/"0"), Theme ("Light"/"Dark"), PaletteUsage (source: MainWindow.xaml.cs:20, 28; RestoreWindowBounds/SaveWindowBounds/ApplySavedTheme/Theme_Click/SavePaletteUsage)
  • 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_)
  • 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)

2. Connecting to the server (ConnectView + ServerSession)

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

3. Administrator two-factor authentication (TOTP)

  • Secret stored under HKLM\SOFTWARE\hMailServer, value AdminTotpSecret, as base64 of a machine-scope DPAPI blob (no entropy) - the same value the retired hMailServer Administrator used, so an existing 2FA setup carries over (source: Services/Totp.cs:3-7, 166-167, 212-241)
  • RFC 6238 TOTP: HMAC-SHA1, 30-second period, 6 digits, accepted window +/-1 step (source: Totp.cs:17-21, 49-57)
  • Prompt appears after a successful COM authentication whenever a secret is configured; 3 attempts, then back to the Connect screen; Cancel also returns to Connect (source: MainWindow.xaml.cs VerifyTwoFactor(), OnConnected())
  • Prompt dialog title "Two-factor authentication", text "Enter the 6-digit code from your authenticator app:" (source: Views/TotpPromptDialog.cs:30, 40)
  • Setup dialog title "Two-factor authentication setup"; shows a QR code (QRCoder) for otpauth://totp/hMailServer%20Control%20Panel?secret=<base32>&issuer=hMailServer&digits=6&period=30 and the base32 key in 4-char groups; enabling or disabling requires a valid current code; button reads "Enable two-factor authentication" / "Disable two-factor authentication" (source: Views/TotpSetupDialog.cs:65, 159-176, 208-236; Totp.cs:60-65)
  • Writing the secret needs local administrator rights (HKLM); the dialog reports "Changing two-factor authentication settings requires administrator rights. Restart the Control Panel as an administrator and try again." on UnauthorizedAccessException (source: TotpSetupDialog.cs:239-242)
  • Reachable from the Connect screen link and from Access & abuse protection > Administrative access > "Two-factor" tab (button "Set up or turn off two-factor authentication…") (source: ConnectView.xaml:54; Views/ServerSettingsView.xaml.cs BuildAdminAccess ~2700-2735)
  • Applies only to Control Panel sign-in, not to the REST API or COM scripts (source: ServerSettingsView.xaml.cs BuildAdminAccess card blurb "Two-factor authentication")
  • 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)

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

  • The sidebar tree is built from Services/NavigationMap.cs (data, WPF-free, tested); MainWindow contains no page names (source: MainWindow.xaml.cs BuildNavTree() comment; MainWindow.xaml:96-98)
  • Groups are expanded by default and carry a Fluent icon; every node's tool tip is "{Title} - {Purpose}"; automation ids nav-<key> for pages and navgroup-<slug> for groups (source: MainWindow.xaml.cs BuildNavItem())
  • Search: a "Search Ctrl+K" button above the tree and the Ctrl+K shortcut open the command palette; both only work while connected (source: MainWindow.xaml:75-94; MainWindow.xaml.cs PreviewKeyDown, ShowPalette())
  • Palette placeholder: Search, or say what you want to do - "stop spam", "block an IP"; footer "↑↓ move Enter open Esc close"; no-match hint suggests an outcome phrase, a page name, or an hMailServer.INI key (source: Views/NavigationPalette.cs:105, 150, 189-190)
  • Palette result sections: "Recently visited", "Most used", "Tasks", "Pages", "Settings" (source: Services/PaletteSearch.cs:97-101)
  • The palette searches page titles + aliases + legacy paths, outcome phrases from Services/IntentIndex.cs (e.g. "stop spam" -> antispam, "block an ip" -> ipranges, "someone is trying to guess passwords" -> autoban, "use microsoft 365 or google sign-in" -> authentication), and the generated settings index of 347 entries (label, INI key or COM path, owning page) produced by build/generate-settings-index.ps1 (source: PaletteSearch.cs class summary; IntentIndex.cs:67-227; Services/SettingsSearchIndex.g.cs header and grep -c = 347)
  • 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)

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

Registered page factories: 54 keys (source: MainWindow.xaml.cs RegisterPages() ~103-159). The tree (source: NavigationMap.cs BuildRoots()):

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)
  • 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 & scripting" (ServerSettingsView Section.Advanced; on-screen "Advanced")
  • scripts "Event scripts" (ScriptsView)
  • hardening "Server limits & expert settings" (FeatureSettingsView Section.Hardening; formerly "Advanced INI settings"/"Advanced hardening", key unchanged)

Top level:

  • about "About" (AboutView)

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

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

6. How settings are read and written

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

7. Page-by-page reference

7.0 Welcome and Dashboard

  • Welcome: title "Welcome", subtitle "Connected to hMailServer {version} on {host}."; six tiles: "Domains & accounts" -> domains, "Server settings" -> protocols, "Dashboard" -> dashboard, "Live logs" -> logs, "Transport security" -> security, "Backup & restore" -> backup (source: Views/WelcomeView.cs:20-49, 113-114)
  • 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 90 samples of history, a table view and High Contrast palette; a "needs attention" card that runs the same ExternalSetupChecks as the External setup page and links each action item to its owning page, with an "External setup…" button (source: Views/DashboardView.xaml:11-133; Views/DashboardView.xaml.cs:15-66, Refresh(), ShowQueue_(), BeginSetupCheck_(), RenderAttention_())

7.1 Monitoring & troubleshooting

  • Server status (status): cards Server (Version incl. VersionArchitecture, State, Started, Uptime), Database (Type via Database.DatabaseType 1 MySQL/MariaDB, 2 MS SQL, 3 PostgreSQL, 4 built-in SQL CE; Host; Name; Schema = Database.CurrentVersion), Statistics (processed, spam removed, viruses removed, SMTP/IMAP/POP3 sessions), Warnings; buttons "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:40-200)
  • Server status warnings computed client-side: no HostName (High); DenyMailFromNull on (High); any IP range with AllowDeliveryFromRemoteToRemote and not RequireSMTPAuthExternalToExternal (Critical, open relay); 127.0.0.1 range that Expires (High, localhost banned); count of expiring (auto-ban) ranges (Medium) (source: StatusView.xaml.cs LoadWarnings() :224-289)
  • Delivery queue (queue): grid of Id, Created, From, Recipients, NextTry, Tries, File from Status.UndeliveredMessages (tab-separated: id, created, from, recipients, next try, file, locked, tries); buttons "View source" (MessageViewerDialog reads the .eml from disk), "Deliver now" (GlobalObjects.DeliveryQueue.ResetDeliveryTime(id) + StartDelivery()), "Remove" (DeliveryQueue.Remove(id)), "Refresh"; search box (source: Views/QueueView.xaml:17-36; Views/QueueView.xaml.cs:40-137)
  • Message trace (messagetrace): title "Message trace"; Address box + "Search" (GlobalObjects.MessageTrace.Search(address), matches sender or recipient, newest first), "Follow this message" (SearchByQueueID), "Remove expired" (DeleteExpired(), window = MessageTraceRetentionDays); columns Time, Event, Sender, Recipient, Status; queue id 0 = refused before queueing. Initial status text says the trace records nothing unless MessageTraceEnabled is set - off by default (source: Views/MessageTraceView.cs:76-78, 152-234; IDL:2362 GlobalObjects.MessageTrace)
  • Live logs (logs): tails the newest hmailserver_*.log in [Directories] LogFolder read from the LOCAL hMailServer.INI, starting at the last 64 KB, polling every 750 ms, keeping 2000 lines, colour-classified (ERROR/Severity 1-2, SMTPD, IMAPD, POP3D, APPLICATION); "Pause"/"Resume" and "Clear"; needs a local server ("Log folder not found on this machine (live logs need a local server).") (source: Views/LogsView.xaml.cs:18, 54-151)
  • Diagnostics (diagnostics): "Run diagnostics" calls Application.Diagnostics with LocalDomainName (pre-filled with the first hosted domain) and TestDomainName (default gmail.com) then PerformTests() and prints [ OK ]/[FAIL] per test; a "Message-store consistency" card reads the local recovery report (MessageStoreConsistencyReport.FileName in the log folder) written by the server's MessageStoreConsistencyCheck task (start-up + hourly), with "Refresh"/"Open report"; on a remote session it says the number is also published as metric hmailserver_messagestore_missing_files (source: Views/UtilityViews.cs DiagnosticsView ~436-851; IDL:1676 Diagnostics)
  • MX query (mxquery): calls Application.Utilities.ResolveMXRecords(domain) - the server's own resolver, honouring DNSServer in hMailServer.INI (issue #29 replaced an nslookup shell-out); "Query"/"Copy" (source: UtilityViews.cs MxQueryView RunQuery(); IDL:1196)
  • External setup (externalsetup): read-only checklist with four states Done / Not needed / Action needed / Cannot tell, each item linking to its owning page via "Settings…"; DNS TXT checks run in the background via the Windows resolver; items: Windows service account (-> hardening), DKIM public key in DNS (-> domains), ARC inbound filtering trusted sealers (-> antispam), ARC sealing needs a domain DKIM key (-> security), MTA-STS policy hosting (-> webservices), TLS-RPT sender address (-> security), DANE TLSA for MX hosts (-> security), TLS private key passphrase (-> certs), ACME reachability (-> acme), inbound client certificates CA bundle (-> ports), OAuth2/XOAUTH2 key material (-> authentication), client autoconfiguration listener + DNS names (-> webservices) (source: Views/ExternalSetupView.cs:20-68, 153-190; Services/ExternalSetupChecks.cs:504-1493)
  • DNS records (dnsrecords): per hosted domain (combo), generates SPF (from Settings.HostName), DKIM (public key derived from the domain's configured private key file), DMARC (_dmarc.<domain>, starting policy p=none), MTA-STS (what the server will serve, from INI MtaStsHostingEnabled default 1, WebServicesHttpsPort, MtaStsPolicyMode default enforce, MtaStsPolicyMaxAge default 604800, MtaStsPolicyMx) and TLS-RPT (uses TlsRptFromAddress), plus PTR guidance; each record has copy buttons and a "Check" against the Windows resolver with a four-way verdict (not published / different / correct / lookup failed); the MTA-STS policy fetch uses HttpClient with redirects disabled and a 10 s timeout (source: Views/DnsRecordsView.cs:30-85, 971-992, 1258-1261)
  • Logging (logging, ServerSettingsView): one tab with cards "Log categories" (COM Logging.Enabled, LogApplication, LogSMTP, LogIMAP, LogPOP3, LogTCPIP, LogDebug, AWStatsEnabled, KeepFilesOpen, Logging.Device combo "Files on disk"/"Database (SQL)", Logging.LogFormat combo "hMailServer (tab separated)"/"NCSA Common Log Format", INI JsonLogging default 0), "Log detail" (INI LogLevel default 9, MaxLogLineLen default 500, SepSvcLogs default 0), "Log retention" (INI LogDeleteDays default 0), "Message trace" (INI MessageTraceEnabled default off, MessageTraceRetentionDays default 30), "Log files" (shows [Directories] LogFolder and "Open log folder" - local only) (source: ServerSettingsView.xaml.cs BuildLogging :2029-2192)
  • API & monitoring (api, FeatureSettingsView Integration): title "API & monitoring"; cards: "REST administration API + Web Control Deck" (link to apikeys; RestApiPort default 0 = disabled, placeholder 8045; RestApiBindAddress default 127.0.0.1; RestApiCertificateFile, RestApiPrivateKeyFile optional, falls back to the ACME certificate; "TLS is required unless the listener is bound to 127.0.0.1"), "Monitoring" (MetricsServerPort default 0, MetricsServerBindAddress default 127.0.0.1, MetricsServerAuthToken, MetricsServerAuthUsername, MetricsServerAuthPassword, MetricsServerCertificateFile, MetricsServerPrivateKeyFile, OtelEndpoint, OtelMetricsEndpoint, OtelLogsEndpoint, OtelMetricsInterval default 60, OtelServiceName default hmailserver, SlowQueryLogMilliseconds default 0) with five live warnings (invalid IPv4 bind, non-loopback bind without credential -> /metrics answers 503, half a Basic pair, half a TLS pair, credential without TLS), "Windows Event Log" (WindowsEventLogEnabled default 1, WindowsEventLogLevel default 2 with options 1-4), "ManageSieve (RFC 5804)" (ManageSieveServerPort default 0, placeholder 4190; ManageSieveServerBindAddress default 127.0.0.1; live STARTTLS-availability state), "Operability" (ShutdownDrainSeconds default 0) (source: FeatureSettingsView.xaml.cs:1794-2133; IniFileSettings.cpp:426, 453, 591, 628)
  • REST API keys (apikeys): title "REST API keys"; file-based - reads/creates/revokes hMailServerApiKeys.ini beside hMailServer.INI (one [Key.<id>] section with Label, Expires "yyyy-MM-dd HH:mm:ss", AllowedFrom, Scope, Domains, Hash written LAST); token format hmapi_ + 64 lower-case hex (32 random bytes), only its SHA-256 hex is stored; a created key is shown once ("Copy this key now" card) and cleared on leaving the page; scope fails closed - only the literal "full" is read/write, everything else read-only; default lifetime 90 days, label max 64 chars; the server re-reads the file on every request so no restart; works while the REST listener is off; LOCAL machine only (needs the INI path) (source: Views/ApiKeysView.cs:20-40, 63-70; Services/ApiKeyStore.cs:75-137, 329-340)

7.2 Accounts & domains

  • Domains (domains): on-screen "Domains & accounts"; left list of domains (search, "Properties", "Add domain" with box newdomain.com), accounts of the selected domain (search, "Edit", "Delete", create with address + password + "Generate" + "Create account"), Aliases panel (alias@domain -> target@anywhere, "Add"/delete), Distribution lists panel ("Properties", "Members", "Add"); all via Application.Domains (source: Views/DomainsView.xaml:16-272; Views/DomainsView.xaml.cs)
  • Domain dialog: title "Domain - {name}"; tabs General, Names, Limits, Signature, Relay, Out of office, DKIM; writes Active, Name, Postmaster, ADDomainName (Active Directory link), MaxSize, MaxMessageSize, MaxAccountSize, 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), first/last name, New password with "Generate strong password" and a strength meter, Last logon; writes Active, Address, AdminLevel, MaxSize, 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: Enabled, Name, POP3 server, Port (default 110), user/password, Minutes between downloads (15), Days to keep (0), Connection security combo 0 None(110)/1 SSL-TLS(995)/2 STARTTLS optional/3 STARTTLS required (replaces the old UseSSL checkbox that could not express STARTTLS), UseAntiSpam, UseAntiVirus (source: CollectionSpecs.cs:203-252)
  • Account "App passwords" tab: account.AppPasswords collection; columns Name, State, Issued, Last used; buttons Create, Revoke / restore, Delete, Refresh; a generated password is shown once (server stores only a hash) (source: Views/AppPasswordsPanel.cs:7-22, 156, 194-255)
  • Account "Two-factor" tab: per-mailbox TOTP enrolment with a QR code shown once; enrolling means the account password stops authenticating mail clients, so app passwords are required (source: Views/AccountTwoFactorPanel.cs:7-20)
  • Account "Rules" tab: per-account rules (add/rename/enable/delete; criteria and actions via the embedded RulesView) (source: CollectionSpecs.cs:254-272; RulesView.xaml.cs ConfigureForRules)
  • Distribution list dialog: title "Distribution list - {address}"; writes Active, Address, Mode, RequireSMTPAuth, RequireSenderAddress, ModeratorAddress, BounceAddress; members edited in "Recipients - {list}" dialog (source: Views/DistributionListDialog.cs:35; Views/RecipientsDialog.cs:31; property grep)
  • "Browse Active Directory" dialog: lists forest domains, searches users (Account/Name/E-mail), single or multi select; used to fill an account's AD fields and to bulk-import members (source: Views/ActiveDirectoryPickerDialog.cs:54, 188-190)
  • Public folders (publicfolders): lists Settings.PublicFolders; "Add folder" (prompt), "Permissions" (double-click too), "Delete" (confirms "and all messages in it"), "Refresh" (source: Views/PublicFoldersView.cs:42-165)
  • Folder permissions dialog "Permissions - {folder}": ACL entries per account or group with eACLPermission bits Lookup 1, Read 2, ..., Insert/append 16, Post 32, ..., Expunge 512, Administer 1024 (source: Views/FolderPermissionsDialog.cs:66-79, 95, 263-267)
  • Groups (groups): Settings.Groups list plus a "Members…" button opening "Members - {group}" (resolves member AccountIDs to addresses; "Add account to group") - the member editor did not exist before, so groups granted folder rights to nobody (source: CollectionSpecs.cs:128-147, 285-386; Views/GroupMembersDialog.cs:49, 300; IDL:655)

7.3 Mail flow & delivery

  • Delivery of e-mail (delivery): tabs Delivery (COM SMTPNoOfTries, SMTPMinutesBetweenTry, MaxNumberOfMXHosts, INI MXTriesFactor default 0, COM SMTPDeliveryBindToIP, SMTPConnectionSecurity combo, AddDeliveredToHeader, INI QuickRetries 0, QuickRetriesMinutes 6, QueueRandomnessMinutes 0, MaxOutboundPerDestinationPerMinute 0), Relayer ("SMTP relayer (smart host)": SMTPRelayer with host1|host2 failover, SMTPRelayerPort, SMTPRelayerConnectionSecurity, SMTPRelayerRequiresAuthentication, SMTPRelayerUsername, password via SetSMTPRelayerPassword; card "OAuth2 for the relay (Microsoft 365)": INI OutboundOAuth2Hosts default smtp.office365.com, OutboundOAuth2TokenUrl, OutboundOAuth2ClientId, OutboundOAuth2ClientSecret, OutboundOAuth2Scope, OutboundOAuth2FixedToken, FetchOAuth2Hosts default outlook.office365.com), Rules (RuleLoopLimit) (source: ServerSettingsView.xaml.cs BuildDelivery :1255-1382)
  • Routes (routes): grid Domain/Target host/Port/Retries/Auth from Settings.Routes; inline add (domain, host, port default 25; NumberOfTries 3, MinutesBetweenTry 10), "Edit" opens "Route - {domain}" with tabs General, Delivery, Addresses, Security, Authentication writing TargetSMTPHost/Port, Description, NumberOfTries, MinutesBetweenTry, AllAddresses, TreatSenderAsLocalDomain, TreatRecipientAsLocalDomain, ConnectionSecurity, RelayerRequiresAuth, RelayerAuthUsername, route addresses (source: Views/RoutesView.xaml.cs; Views/RouteDialog.cs:49-70; property grep)
  • Incoming relays (relays): title "Incoming relays", subtitle "Upstream gateways (spam filters, load balancers) whose IP addresses should not count as the connecting client in anti-spam host checks."; Settings.IncomingRelays Name/LowerIP/UpperIP add/delete (source: UtilityViews.cs:15-33, 100-185)
  • Rules (rules): on-screen "Global rules", subtitle "Server-wide message rules, evaluated top to bottom."; Application.Rules; grid #/Name/Enabled; buttons Move up, Move down, Enable / disable, Delete rule, Add rule; IF criteria (Add…/Edit…/Remove) and THEN actions (Add…/Edit…/Remove/Up/Down) (source: Views/RulesView.xaml:18-108; Views/RulesView.xaml.cs:31, 249-420)
  • Rule action types offered: Delete e-mail 1, Forward e-mail 2, Reply 3, Move to IMAP folder 4, Run script function 5, Stop rule processing 6, Set header value 7, Send using route 8 (server-level only), Create copy 9, Bind to address 10 (server-level only) (source: Views/RuleActionDialog.cs:73-84)
  • Server messages (servermessages): Settings.ServerMessages Name/Text, fixed set (no add/delete) (source: CollectionSpecs.cs:149-162)
  • Server sendout (sendout): wildcard (default *), from address/name, subject, body -> Utilities.EmailAllAccounts(...); reports "The server did not queue the sendout" when the call returns false (source: UtilityViews.cs SendoutView; IDL:1177)

7.4 Spam & virus filtering

  • Spam filtering overview (spamoverview): read-only; reads Settings.AntiSpam thresholds, headers, HELO/PTR/MX checks and scores, SPF/DKIM/DMARC and scores, SpamAssassin, greylisting and bypasses, blocked-sender count; judgement in Services/SpamPipeline.cs; every row links to the owning page; "Refresh" (source: Views/SpamOverviewView.cs:21-40, 126-156)
  • Quarantine (quarantine): Settings.AntiSpam.Quarantine (Refresh(), Count, indexer pages the table); columns Held, Sender, Recipients, Score, Subject, row tooltip "Held because: {reason}"; buttons "Release" (ReleaseByDBID), "Delete" (DeleteByDBID, confirmation defaults to No - the only copy), "Remove expired" (DeleteExpired(), window QuarantineRetentionDays), "Refresh"; empty state text says quarantining is off unless QuarantineEnabled is set (source: Views/QuarantineView.cs:14-33, 87-166, 241-360; IDL:2595 "Empty and inert unless QuarantineEnabled is set in hMailServer.ini")
  • Anti-spam settings (antispam): on-screen "Anti-spam"; tabs General ("Thresholds & actions": AntiSpam.SpamMarkThreshold, SpamDeleteThreshold, AddHeaderSpam, AddHeaderReason, PrependSubject, PrependSubjectText, MaximumMessageSize; "Quarantine instead of refusing": INI QuarantineEnabled off by default, QuarantineRetentionDays default 30), Sender auth (UseSPF+UseSPFScore, INI SpfVoidLookupLimit, DKIMVerificationEnabled+DKIMVerificationFailureScore, INI DkimAcceptSha1 off, DMARCEnabled+DMARCFailureScore, INI DmarcTreeWalkEnabled, INI DmarcRptSchemaVersion default 1, COM ArcFilteringEnabled + ArcTrustedSealers - "does nothing at all" with an empty sealer list), Host checks (CheckHostInHelo(+Score), CheckPTR(+Score), UseMXChecks(+Score)), Greylisting (GreyListingEnabled, GreyListingInitialDelay, GreyListingInitialDelete and FinalDelete shown in days (stored hours, Divisor 24), BypassGreylistingOnMailFromMX, BypassGreylistingOnSPFSuccess, INI GreylistingEnabledDuringRecordExpiration default 1, GreylistingRecordExpirationInterval default 240, button "Clear greylisting triplets"), SpamAssassin (SpamAssassinEnabled/Host/Port/MergeScore/Score, "Test SpamAssassin connection", INI SAMinTimeout 30, SAMaxTimeout 90, SAMoveVsCopy off; card "External filtering engine (rspamd and anything else that speaks HTTP)": INI FilterHookUrl (empty = none), FilterHookTimeoutSeconds 10, FilterHookFailClosed off, FilterHookRejectScore 100, FilterHookMaxMessageSizeKB 10240) (source: ServerSettingsView.xaml.cs BuildAntiSpam :1384-1665; IDL:2583-2586)
  • SURBL servers (surbl): AntiSpam.SURBLServers Active/DNSHost/RejectMessage/Score (default 5) (source: CollectionSpecs.cs:38-51)
  • DNS blacklists (dnsbl): AntiSpam.DNSBlackLists Active/DNSHost/ExpectedResult/RejectMessage/Score (default 5) (source: CollectionSpecs.cs:53-67)
  • White list (spamwhitelist): on-screen "Anti-spam white list"; AntiSpam.WhiteListAddresses LowerIPAddress/UpperIPAddress/EmailAddress/Description (source: CollectionSpecs.cs:69-82)
  • Blocked senders (blockedsenders): AntiSpam.BlockedSenders Address (exact with @, else domain + subdomains)/Score default 100/Description; matches the CLAIMED sender - not anti-spoofing (source: CollectionSpecs.cs:84-100)
  • Greylisting white list (greylistwhitelist): AntiSpam.GreyListingWhiteAddresses IPAddress/Description (source: CollectionSpecs.cs:102-113)
  • Virus scanning overview (virusoverview): read-only; reads ClamAV/ClamWin/custom scanner settings; exists because VirusScanner::ScanFile_ treats "every scanner errored" as NoVirusFound, so an enabled-but-broken scanner looks like a clean scan; judgement in Services/VirusPipeline.cs (source: Views/VirusOverviewView.cs:21-44, 131-139)
  • Anti-virus settings (antivirus): on-screen "Anti-virus"; tabs General (AntiVirus.Action combo, NotifySender, NotifyReceiver, MaximumMessageSize, EnableAttachmentBlocking; card "When a scanner cannot run": INI AVFailAction 0 deliver / 1 hold (default 0), AVFailRetryMinutes 15, AVFailMaxHolds 16), ClamAV (ClamAVEnabled/Host/Port, "Test ClamAV connection", INI ClamMinTimeout 15, ClamMaxTimeout 90), ClamWin (ClamWinEnabled/Executable/DBFolder, "Auto-detect ClamWin", "Test ClamWin scanner"), Custom (CustomScannerEnabled/Executable/ReturnValue, preset picker, "Test custom scanner") (source: ServerSettingsView.xaml.cs BuildAntiVirus :1667-1777)
  • Blocked attachments (blockedattachments): AntiVirus.BlockedAttachments Wildcard (default *.exe)/Description; requires AntiVirus.EnableAttachmentBlocking on the Anti-virus page (source: CollectionSpecs.cs:115-126)

7.5 Connections & protocols

  • Protocols (protocols): tabs Services (COM ServiceSMTP, ServiceIMAP, ServicePOP3), SMTP (HostName, MaxSMTPConnections, MaxMessageSize, MaxSMTPRecipientsInBatch, WelcomeSMTP, AllowSMTPAuthPlain, DenyMailFromNull, INI RejectFullMailboxAtRcpt default on, INI QuotaWarningPercent default 90, INI ArchiveRetentionDays default 0, INI MetricsPerDomainEnabled default off, AllowIncorrectLineEndings, DisconnectInvalidClients, MaxNumberOfInvalidCommands), IMAP (MaxIMAPConnections, WelcomeIMAP, IMAPIdleEnabled, IMAPQuotaEnabled, IMAPSortEnabled, IMAPACLEnabled, IMAPSASLPlainEnabled, IMAPSASLInitialResponseEnabled, IMAPPublicFolderName, IMAPMasterUser, IMAPHierarchyDelimiter; card "IMAP search limits": INI IMAPSearchTimeout 60, IMAPSearchMaxMegabytes 2048; card "Change history for synchronising clients": INI IMAPExpungeRetentionRecords 5000; card "Repair": button "Recalculate folder UID counters"), POP3 (MaxPOP3Connections, WelcomePOP3; card "How often a client may check for mail": INI Pop3LoginDelaySeconds 0), Timeouts (INI SMTPDMinTimeout 10, SMTPDMaxTimeout 1800, SMTPCMinTimeout 30, SMTPCMaxTimeout 600, POP3DMinTimeout 10, POP3DMaxTimeout 600, POP3CMinTimeout 30, POP3CMaxTimeout 900) (source: ServerSettingsView.xaml.cs BuildProtocols :1036-1253; IniFileSettings.cpp:641, 643, 656)
  • TCP/IP ports (ports): grid of Settings.TCPIPPorts Protocol (1 SMTP/3 POP3/5 IMAP), Address, Port, Security (None / SSL/TLS / STARTTLS (optional) / STARTTLS (required)), Certificate name, and a live "Listening"/"Not listening"/"Server off"/"Unknown" badge from the local TCP table (ListenerProbe, local sessions only) with a summary line naming unbound ports; inline add (protocol, address, port, security), "Edit" opens "TCP/IP port" dialog (writes Protocol, Address, PortNumber, ConnectionSecurity, SSLCertificateID, ClientCertificatePolicy, ClientCertificateCAFile - mutual TLS), "Restore defaults" = TCPIPPorts.SetDefault() (SMTP 25 + 587, POP3 110, IMAP 143, no security), Delete (source: Views/TcpIpPortsView.xaml.cs:15-34, 55-175, 291-323; Views/TcpIpPortDialog.cs:13-36; property grep)
  • Web services & autoconfiguration (webservices): on-screen "Web services & client autoconfiguration"; cards "Listener" (WebServicesHttpPort default 0, WebServicesHttpsPort default 0 - "Nothing below is served until a port is set here", WebServicesBindAddress default 0.0.0.0, WebServicesCertificateFile/WebServicesPrivateKeyFile optional, fall back to ACME), "Client autoconfiguration (autoconfig & autodiscover)" (AutoconfigEnabled default 1, AutoconfigClientHost), "MTA-STS policy hosting" (MtaStsHostingEnabled default 1, MtaStsPolicyMode default enforce, MtaStsPolicyMaxAge default 604800, MtaStsPolicyMx), "Calendar and contacts discovery (CalDAV / CardDAV)" (CalDavRedirectUrl, CardDavRedirectUrl - redirects only; "hMailServer does NOT implement CalDAV or CardDAV") (source: FeatureSettingsView.xaml.cs:2668-2760; IniFileSettings.cpp:602-611)
  • DNS resolver (dns): cards "Name servers" (DNSServer - single IPv4, port 53; empty = Windows resolvers), "DNS cache" (UseDNSCache default 1 - toggles DNS_QUERY_BYPASS_CACHE, i.e. Windows' cache, ignored when DNSServer is set), "DNS blacklist checks" (DNSBLChecksAfterMailFrom default 1) (source: FeatureSettingsView.xaml.cs:2624-2666; IniFileSettings.cpp:249, 400)

7.6 TLS & certificates

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

7.7 Access & abuse protection

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

7.8 Maintenance

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

7.9 About

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

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

Page / card Gate Default Source
Message trace page; Logging > "Message trace" card INI MessageTraceEnabled 0 IniFileSettings.cpp:626; MessageTraceView.cs:76-78
Quarantine page; Anti-spam > "Quarantine instead of refusing" INI QuarantineEnabled 0 IniFileSettings.cpp:624; IDL:2595
API & monitoring > REST API; REST API keys page (works while the listener is off) INI RestApiPort 0 IniFileSettings.cpp:591; ApiKeysView.cs:31-35
API & monitoring > Monitoring INI MetricsServerPort; Otel*Endpoint 0; empty IniFileSettings.cpp:426; FeatureSettingsView.xaml.cs Monitoring card
API & monitoring > ManageSieve INI ManageSieveServerPort 0 IniFileSettings.cpp:453
Certificates (ACME) INI AcmeEnabled 0 IniFileSettings.cpp:595
Web services & autoconfiguration (all cards) INI WebServicesHttpPort / WebServicesHttpsPort 0 / 0 IniFileSettings.cpp:602-603; card "Nothing below is served until a port is set here"
Authentication > OAuth2 INI OAuth2Enabled 0 IniFileSettings.cpp:208
Directory authentication (LDAP) INI [LDAP] Enabled 0 LdapSettings.cpp:229
Directory synchronisation > Unattended synchronisation INI [LDAP] SyncScheduleMinutes 0 LdapSettings.cpp:322
Transport security > ARC sealing, SRS, BATV, Authentication-Results, TLS-RPT, DMARC rua ArcSealingEnabled, SRSEnabled, BATVEnabled, AuthenticationResultsEnabled 0; TlsRptFromAddress, DmarcRptFromAddress empty off IniFileSettings.cpp:455, 480, 614, 616; FeatureSettingsView.xaml.cs Security cards
Anti-spam > ARC inbound filtering COM ArcFilteringEnabled + non-empty ArcTrustedSealers does nothing with an empty list IDL:2583-2586
Anti-spam > External filtering engine (rspamd) INI FilterHookUrl empty ServerSettingsView.xaml.cs BuildAntiSpam
Auto-ban > Per-name lockout INI AccountLockoutThreshold 0 (page default) ServerSettingsView.xaml.cs BuildAutoBan
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 (source: IniFileSettings.cpp:161, 236, 249, 400-404, 584, 607, 611, 628, 641)

9. Contradictions and stale claims between documents and code

  • hmailserver documentation/hmailserver-documentation.md §5 (line 328) and §19 (line 1210) and settings.md line 3 say the Control Panel is a .NET 8 app / needs the .NET 8 Desktop Runtime. Code: net10.0-windows (ControlPanel.csproj:5); About page says .NET 10 (AboutView.cs:85); README.md:111 and :130 say .NET 10.
  • Documentation §5.3 (lines 353-367) and §20 (lines 1322-1362) describe the OLD tree: groups Status / Domains / Rules / Settings (with Anti-spam ▸ Anti-virus ▸ Logging ▸ Security ▸ Network ▸ Maintenance) / Utilities, and pages "Settings → Auto-ban & SSL/TLS" and "Settings → Advanced hardening". Code: eight task-based groups (section 5 above); "Auto-ban & SSL/TLS" is split into "Auto-ban" and "SSL/TLS"; "Advanced hardening" is now titled "Server limits & expert settings" (NavigationMap.cs BuildRoots and comments). The doc's statement (line 1207) that the tree "deliberately mirrors the old Administrator's layout" is what NavigationMap.cs explicitly replaced.
  • Documentation §20 lists 38 pages; code registers 54. Absent from the doc: 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 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 347 entries across both views. Its Section A table lists "Auto-ban & SSL/TLS" as one page and files "admin password" under "Advanced & scripting"; both have moved (AdminAccess section, ServerSettingsView.xaml.cs:2568).
  • settings.md Section A says ServerSettingsView pages "mirror the classic Administrator" - superseded by the task-based map.
  • Documentation §5.2 and Views/WelcomeView.cs:11 ("Landing page shown after connecting") imply Welcome is the landing page; code navigates to the Dashboard after connecting (MainWindow.xaml.cs OnConnected: NavigateTo("dashboard")).
  • Documentation §20 "Dashboard: Live graphs — throughput, sessions, service state": the dashboard has no service-state readout (that is on Server status); it has five KPIs, two charts and the external-setup summary (DashboardView.xaml).
  • Documentation §20 "Incoming relays: Trusted forwarders whose Received headers are believed" vs code subtitle "whose IP addresses should not count as the connecting client in anti-spam host checks" (UtilityViews.cs:33).
  • Documentation §20 "Blocked attachments: Refuse attachments by file extension" - code strips matching wildcards and only when AntiVirus.EnableAttachmentBlocking is on (CollectionSpecs.cs:118).
  • NavigationMap.cs class summary says "Of 42 pages, 30 sat under a single Settings group" - historical; 54 pages are registered today (internal comment, not user-facing).
  • Totp.cs header comment (line 5-7) says the secret is stored "exactly like hMailServer Administrator" - consistent with documentation line 1209 ("TOTP secret carries over"); no contradiction, recorded as confirmed.

10. Unconfirmed / not verified here

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

Home — full index

Start here

1. Install and run

2. Secure it

3. Operate it

4. Extend it

5. Contribute to it

Look it up — from any journey


About this wiki

Clone this wiki locally