Skip to content
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ
  • 0 Votes
    1 Posts
    119 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) is a security release that bears on the send-as and sign-in sections here. An authenticated submission is now DKIM-signed as another domain hosted here only when its account may send as the address its From names (its own address, an alias pointing at it, a mailbox that granted it the post right, an address it proved, or a masked address of its own, the addresses the send-as table below lists); otherwise the mail is still accepted and sent, unsigned as that domain, so give such an account the alias or the grant before upgrading. A domain's or an account's rule replies only from an address its owner may send as. SMTP, POP3 and ManageSieve refuse a SASL PLAIN sign-in that asks to act as another account; with SRS on, mail to an SRS address this server handed out is held to the relay rules unless it is a bounce; the IMAP and POP3 listeners can take the client's address from a PROXY protocol header as the SMTP listener does, each from a list of peers of its own, both off by default; and the Control Panel's IP ranges page, which could show no list of ranges at all, is fixed. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) is a security release that bears on the auto-ban, the lockout and the IP ranges here. The auto-ban saves the IP range it adds under a name of its own, the user name cut so that the name and its suffix fit in the 100 characters a range's name holds, so that it bans the address: up to 6.3.7 a second or later ban for a long user name could be refused (HM6099) and the address given a whole new set of attempts, and now an address's counted failures are cleared only once its range is saved. A sign-in's name is matched to its account as the per-name lockout counts it, so a mail client that signs in with another spelling of the account's address than the stored one, in more than the case of the letters A to Z, or with white space at either end, may no longer sign in, and each such try is counted as a failure: correct the name in the client. With the web services listener on, an address whose IP range allows none of SMTP, POP3 and IMAP, an auto-ban's or one of your own, is now refused there too, before the TLS handshake, loopback and the addresses in WebServicesTrustedProxies apart; such a range of your own closes to its addresses ACME's http-01 validation when this listener answers it, MTA-STS, CalDAV, CardDAV, Autodiscover, autoconfiguration and the Web Key Directory, so before upgrading check that the certificate authority, the sending MTAs and these services' clients do not come from one. See the 6.3.8 announcement. This topic covers who may use this server and how hard it is to abuse it. Know this first: only the IP ranges and auto-ban work out of the box; every other brake here is off until you set it. Until you do, nothing else stops a botnet spending one guess per address, or a stolen password sending thousands of messages. The secure defaults of a new installation, TLS for sign-in and compromised-account detection are in Hardening a public mail server. Where each one is set Since 6.3.4 the database is the settings store, and for a key it holds, a [Settings] line edited in hMailServer.ini changes nothing; on 6.3.3 these keys were lines of the file, applied at the next start. The store, and when a save takes effect, are in the settings topic. What Kept in Set with Takes effect IP ranges the database Control Panel Access & abuse protection → IP ranges; the Control Deck's IP ranges view; /api/v1/ipranges; hmctl iprange; COM Settings.SecurityRanges at once Auto-ban's four settings the database Control Panel Access & abuse protection → Auto-ban; the Deck's settings; PUT /api/v1/settings; COM at once Lockout, tarpits, rate limit, send-as check, hashing, policy the settings store, under their [Settings] names Control Panel; PUT /api/v1/settings/ini/{name}; hmctl ini set <name> <value>; COM Settings.SetIniSetting; on Linux hmailserver --set-setting NAME=VALUE when saved [SendingLimits], [SendingLimitsOverrides], PasswordPepper hMailServer.ini only the file, by hand or through the Control Panel (below) sending limits within seconds; the pepper after a restart The Control Panel and COM are Windows-only. Of these [Settings] keys the Deck edits only SmtpTarpitCount and SmtpTarpitDelaySeconds. A value set with hmailserver --set-setting while the service runs waits for the next save, reinitialise or restart. IP ranges: priority and the relay grid When a client connects, every range containing its address is found and the highest priority wins. The shipped ranges are Internet (priority 10, 0.0.0.0–255.255.255.255) and My computer (priority 30, 127.0.0.1); a range added on the Control Panel's page starts at 15, so a narrow range sits in front of Internet without editing it. At every RCPT TO two decisions are made, in order: may this combination be delivered at all (if not, 550 Delivery is not allowed to this address.), and must the client authenticate first (if so, 530 SMTP authentication is required.). "Local" and "remote" describe the addresses, not the client: an address is local when its domain is a hosted active domain or a route says so. Combination REST fields: allowed / auth required Internet My computer Local → local deliver_local_to_local / require_auth_local_to_local allowed, auth required allowed, no auth Local → remote deliver_local_to_remote / require_auth_local_to_remote allowed, auth required allowed, no auth Remote → local deliver_remote_to_local / require_auth_remote_to_local allowed, no auth allowed, no auth Remote → remote (relay) deliver_remote_to_remote / require_auth_remote_to_remote allowed, auth required allowed, auth required Anti-spam, anti-virus spam_protection, virus_protection on on Require TLS for auth require_tls_for_auth on in a new 6.3.4 installation; otherwise as the server had it off Remote → remote is the open-relay box. Both shipped ranges allow it with authentication required, even from 127.0.0.1, so a client that has not signed in gets 530; a range that does not allow it at all gets 550. The Control Panel's Server status page raises a Critical warning for any range that allows it without authentication. Remote → local is inbound mail and must need no authentication. Local → local needs it on Internet so that nobody outside can forge mail from one of your addresses to another. The protection switches reach further than their names. Clearing Enable anti-spam turns off every spam test, greylisting and the recipient tarpit for the range; clearing Enable anti-virus means its mail is never flagged for scanning. Since 6.3.4 SPF gives loopback no pass, so a content filter that re-injects mail should authenticate or use a range with anti-spam off. Require TLS for auth belongs to the range, not the port. With it set, AUTH is not advertised on a cleartext connection from that range, on SMTP, POP3, IMAP and ManageSieve. A device that cannot sign in gets a range of its own above Internet. For a scanner at 192.0.2.50 that sends from your domains to your own mailboxes only: hmctl iprange create --set name=scanner --set lower=192.0.2.50 --set upper=192.0.2.50 \ --set priority=20 --set allow_smtp=true --set allow_imap=false --set allow_pop3=false \ --set deliver_local_to_local=true --set require_auth_local_to_local=false \ --set deliver_local_to_remote=false --set deliver_remote_to_local=false \ --set deliver_remote_to_remote=false --set spam_protection=true --set virus_protection=true hmctl iprange update <id> and PUT /api/v1/ipranges/{id} (since 6.3.3) change any subset of the fields. The routes refuse a domain-restricted API key; a role needs manage_settings. The Control Panel's Restore defaults (SecurityRanges.SetDefault()) discards every range, your own and the auto-ban ones, and writes back the two shipped ones as the create scripts make them, without Require TLS for auth. Auto-ban, and why its ranges sit at priority 100 Auto-ban counts failed logons per connecting address; SMTP AUTH, POP3, IMAP, ManageSieve, the REST API and the web services listener's CardDAV, CalDAV and Autodiscover sign-ins feed one counter, but since 6.3.8 neither REST, for a failure from loopback (all of 127.0.0.0/8, and ::1), nor the web services listener, for one from loopback or an address in WebServicesTrustedProxies, counts it towards a ban of the address: an account's failed sign-in from there is counted against the name alone. Up to 6.3.7 an account's failed sign-in at REST from loopback was counted towards a ban of 127.0.0.1, which could then be refused at the SMTP, POP3, IMAP and ManageSieve listeners; on 6.3.2 to 6.3.7 AutoBanNeverBan with 127.0.0.1, ::1 avoids it. Behind a reverse proxy on the same machine (the Linux package binds REST to 127.0.0.1), the webmail's failed sign-ins are never auto-banned; account lockout, below, is what limits them there, and it is off until you set it. At the limit auto-ban clears that address's counted failures, creates an expiring range named Auto-ban: <user> at priority 100, and disconnects; SMTP says 535 Authentication failed. Too many invalid logon attempts. Priority 100, above both shipped ranges, is what makes a ban effective; AutoBanNeverBan, below, exempts an address. Setting (COM name) Default Trap AutoBanOnLogonFailure on — MaxInvalidLogonAttempts 3 0 disables auto-ban entirely, whatever the switch says MaxInvalidLogonAttemptsWithin 30 minutes Not a sliding window: how long a failure record survives, deleted by a pass every minute that runs only while auto-ban is on AutoBanMinutes 60 0 disconnects the client but creates no range, so nothing is banned Their REST keys include auto_ban_on_logon_failure and max_invalid_logon_attempts. The office profile (since 6.3.4) raises the limit to 5. AutoBanNeverBan (since 6.3.2): addresses and CIDR blocks whose failures are never counted; the per-connection disconnect still applies, and a bad entry is reported once as HM6524. Server status warns "Localhost is currently banned in the IP ranges." when a ban covers 127.0.0.1. Control Panel sign-in over COM is outside auto-ban and lockout, so guessing an administrator's mailbox name cannot lock them out of the tool. Spam traps and the multi-recipient null-sender check ban through the same ranges (Auto-ban: spam trap <address>, Auto-ban: multi-recipient null sender <address> see Allow and block lists and the anti-spam stack. Firewall bans (AutoBanFirewall, AutoBanCommand, fail2ban) are in Reading the logs. Releasing a banned address. Clear logon-failure list (POST /api/v1/settings/logon-failures/clear, COM Settings.ClearLogonFailureList()) clears counted failures only, for every address at once; a ban stays until its range expires. To release one now: Have the person correct the password in their client first: the counted failures were cleared when the ban was made, so a client still retrying the old one reaches the limit again as soon as the range is gone. Find the range: the IP ranges page, the Deck's IP ranges view, hmctl iprange list or GET /api/v1/ipranges. Its name begins Auto-ban: and its priority is 100. Delete it: on the page, hmctl iprange delete <id>, or DELETE /api/v1/ipranges/<id>. With AutoBanFirewall on, the firewall is reconciled on every range deletion, so its rule goes too. Five error-log codes mean the control has quietly stopped working: Code Meaning HM6114 A failed logon could not be recorded, so no ban will trigger while this persists HM6099 The auto-ban range could not be saved: the address has not been blocked HM6113 Spent failures could not be cleared, so the address may be banned again as soon as its ban expires HM6112 Expired IP ranges could not be removed, so an expired ban keeps blocking HM6115 Expired failures could not be cleared, so occasional failures may add up to a ban Account lockout, by name A distributed attack never crosses any one address's threshold. Lockout counts the name being guessed at, across SMTP AUTH, POP3, IMAP, ManageSieve, REST and the webmail's sign-in. It is off until set; the office profile sets 10. AccountLockoutThreshold=10 ; failed logons per username before a lockout. 0 = off AccountLockoutWindowMinutes=30 ; the window they must fall inside AccountLockoutMinutes=30 ; how long the lock lasts It counts names, not accounts, so a nonexistent name locks as a real one would, and reveals nothing. A locked name is refused before the password is checked, with the ordinary invalid-credentials reply; only its timing differs, deliberately. A lockout refusal is not charged to the connecting address, so an owner retrying from an office NAT does not get everyone behind it auto-banned. Passkeys and single sign-on are not counted, being no guesses. To lift one early: POST /api/v1/accounts/{address}/unlock, which a help desk scoped to the account's domain may call, COM Application.UnlockAccount, or Unlock on the Deck's Accounts row. A password reset clears it too. Tarpits and the submission rate limit All 0, off, until set; these values are a starting point. LogonTarpitSeconds=2 ; per failure on this connection, capped at 30 s. 0 = off SmtpTarpitCount=10 ; recipients an unauthenticated session may name before... SmtpTarpitDelaySeconds=5 ; ...each further RCPT TO waits this long, capped at 30 s MaxSubmissionsPerIPPerMinute=30 ; MAIL FROM commands per source IP per minute. 0 = off The logon tarpit (Auto-ban page) makes a failed logon's refusal wait LogonTarpitSeconds times the failures so far on that connection, on SMTP, POP3 and IMAP. A correct password never waits. The recipient tarpit (Anti-spam settings page; COM AntiSpam.TarpitCount and AntiSpam.TarpitDelay; REST tarpit_count and tarpit_delay on /api/v1/settings/antispam) spares authenticated sessions and ranges with anti-spam off. MaxSubmissionsPerIPPerMinute counts every MAIL FROM, authenticated or not, and refuses the excess with 421 Too many messages from your IP address, which contains a compromised account. The office profile sets 60; gateway keeps 0, since all inbound mail comes from the gateway's few addresses. Since 6.3.4 a save restarts its one-minute window. Behind a load balancer, give the SMTP listener SMTPProxyProtocolEnabled with SMTPProxyProtocolTrustedIPs (or XCLIENT), or auto-ban, the rate limit and the ranges all reason about the proxy's address: see Behind a gateway or reverse proxy. Per-account sending limits The brake on a compromised account, counted against the authenticated user name (never MAIL FROM) over a rolling period. The server level stays in the file: [SendingLimits] MaxMessagesPerAccountPerPeriod=500 MaxRecipientsPerAccountPerPeriod=2000 PeriodHours=24 ; clamped to 1-168 StateSaveIntervalSeconds=10 ; clamped to 1-3600 [SendingLimitsOverrides] [email protected]=5000:50000 [email protected]=0:0 Both limits default to 0, no limit; recipients are the stricter measure. An override replaces the global pair for that address, 0:0 exempts it, and an optional third field sets its period in hours; a malformed line is ignored with a log entry and the global limit applies. On Windows edit C:\Program Files\hMailServer\Bin\hMailServer.INI with an elevated editor, or use the Control Panel's Maintenance → Server limits & expert settings on the server itself; on Linux, sudoedit /etc/hmailserver/hMailServer.ini. They are not in the settings store, so no environment variable or setting profile writes them. What the sender sees: 452 4.7.0 Sending limit for this account has been reached. Please try again later. at MAIL FROM, or 452 4.5.3 Recipient sending limit for this account has been reached. Please try again later. at RCPT TO. The session stays open, the refusal spends no budget, and the application log, not the error log, says SendingLimit refused submission. Counters survive a restart in hmailserver_sendinglimits.dat in the data directory. It fails open: an unreadable file, a limiter exception or a full table of tracked accounts (error 5813) lets the message through. Since 6.3.4 there are three levels. A domain and an account may hold sending_max_messages and sending_max_recipients, which tighten the level above and never loosen it; an address's override line is its server level. Set them on the Levels tab of the domain and account dialogs, in the Deck's editors, with PUT /api/v1/accounts/{address}, or hmctl account set <address> sending_max_messages 100 (inherit returns to the level above); they apply at once. Also since 6.3.4 the webmail's send and the send API consume the same quota, answering 429; a scheduled draft is not counted when it goes. Who may send as whom SmtpAuthenticatedSenderCheck (Control Panel Connections & protocols → Protocols, SMTP tab) came in 6.2.25 and is off by default, since an installation may depend on what off allows: any account sending as anyone. The office and gateway profiles set it to 1. Turned on, it applies to every authenticated session, whose MAIL FROM must then be: Kind Given by The account's own address always An alias resolving to it, up to five hops the administrator A mailbox whose owner granted the post (p) right on their INBOX that owner, with any IMAP client's SETACL An address elsewhere proved by a code mailed to it (since 6.3.4) the person, in the webmail's Settings, Other accounts, Send mail as A masked address the account made, while it is on (since 6.3.4) the person, in the webmail's Settings, Masked addresses Anything else is refused 550 5.7.1 before the message is accepted. The webmail's From picker lists exactly these, so what it offers is what the server takes. The null sender is always allowed; unauthenticated sessions are unaffected. The post right counts only while IMAP ACL enforcement is on (enableimapacl, on by default). Before turning the check on, grant p to every delegate who sends as a shared mailbox. Mail sent as a proved outside address is not aligned with that address's domain, so a receiver honouring its DMARC policy may quarantine or refuse it. How passwords are stored In the Control Panel, Access & abuse protection → Authentication, card Password storage. Schemes: 0 none, 1 Blowfish, 2 MD5, 3 SHA-256, 4 PBKDF2-HMAC-SHA256 (the default), 5 Argon2id, 6 DPAPI (stored secrets only), 7 scrypt. PreferredHashAlgorithm=4 ; 3, 4, 5 or 7. Anything else is refused (HM5528) and read as 4 MinimumAcceptedHashAlgorithm=0 ; refuse a logon whose stored hash is weaker. 0 = off PasswordPepper= ; a server-wide secret mixed into Argon2id and scrypt hashes PasswordHashIterations=0 ; PBKDF2. 0 = 210,000. Range 10,000 to 10,000,000 PasswordHashMemoryKB=0 ; Argon2id. 0 = 19,456 KiB. Range 4,096 to 1,048,576 PasswordHashTimeCost=0 ; Argon2id passes. 0 = 2. Range 1 to 20 Hashes upgrade at a successful logon, only upward. Raising the scheme or a work factor re-derives each hash when its owner next signs in; lowering either affects new hashes only. MinimumAcceptedHashAlgorithm set too high is a lockout. The refusal follows a correct comparison and the upgrade needs a successful logon, so an account below the floor never climbs out: its password must be reset. Raise the preference first, let accounts sign in, then set the floor. Argon2id and scrypt are peers, so 5 accepts either. A work factor out of range is reported as HM5561 and read as 0; HM5607 means Argon2id was asked for on a build whose OpenSSL has none, and another scheme is used. Argon2id allocates PasswordHashMemoryKB on a connection thread at every logon. PasswordPepper must be set before accounts exist: changing it invalidates every Argon2id and scrypt password. PBKDF2 is not peppered, so it stays usable for SCRAM. Since 6.3.4 it lives in hMailServer.ini only and no backup carries it, so keep a copy of the value with the backups: a database or archive restored onto a machine whose file has no pepper, or a different one, refuses every Argon2id and scrypt password, and a warm standby needs the same value in its own file. Where the service cannot write the file (a new Windows installation running as NT SERVICE\hMailServer, or a Linux package installation), COM and REST refuse it: set it with an elevated editor (sudoedit on Linux) and restart the service. The password policy All off until set, applied where a password is chosen, never at logon; on Windows under Access & abuse protection → Administrative access, Password tab. PasswordPolicyMinimumLength=12 PasswordPolicyRequireMixedCase=1 PasswordPolicyRequireDigit=1 PasswordPolicyRequireNonAlphanumeric=1 PasswordPolicyRejectCommon=1 ; refuse passwords from a built-in common list PasswordPolicyHistoryCount=5 ; refuse the last N passwords PasswordPolicyMaximumAgeDays=0 ; 0 = passwords never expire The office profile sets a minimum of 10 and the common list. Expiry needs care: IMAP, POP3 and SMTP cannot change a password and /portal refuses an expired one, so an administrator renews it, or its owner through an app password, which still signs in to /portal. Active Directory accounts are exempt. The breach check is in Hardening a public mail server; the rest of sign-in is in Signing in. What it does not do None of it spam-tests your own users: an authenticated session never is. A setting profile never writes an IP range. Where to read more Wiki: Security Hardening sections 17.1, 17.2, 17.5, 17.7 and 17.9; Settings Reference sections 9.1, 9.9 and 10; Stopping Spam section 10.1; Troubleshooting sections 19.3 and 19.7. Documents: SettingsApply.md, SettingLevels.md, SendAs.md, Profiles.md, Roles.md, ServiceAccount.md. Public documentation: SecurityRange and SecurityRanges, for the COM names of the range flags. On this forum: Being trusted by the servers you send to, The Control Deck, Upgrading to 6.3.4, hMailServer on Linux.
  • 0 Votes
    1 Posts
    119 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) changes three things this guide touches. On MySQL and MariaDB a lost database connection is reconnected and the statement run again, so the statement after a database restart, a failover or wait_timeout no longer fails, and an insert that forgives a duplicate key forgives that alone, so an error log that was quiet may now name failures that were hidden. On Windows a stop or a reinitialise no longer waits past a work queue's ten seconds for a thread held by a host or a database that has stopped answering, and setup, the live update's silent install and the uninstaller wait ShutdownDrainSeconds plus 90 seconds for the stop, where they waited plus 30. The ACME HTTP-01 challenge responder, which served one connection at a time, serves up to 64 at once. See the 6.3.7 announcement. This topic describes a running hMailServer 6.3.4 service: its parts and pools, the connection ceilings in front of them, the scheduled work behind them, what the machine needs, and what a folder of a hundred thousand messages costs the webmail. The one thing to know first: nothing in the server is unbounded. There is no thread per connection and no pool that grows. Every piece of work runs on a pool of a set size, and a dependency that stops answering uses up a pool long before it uses up the processor, the memory or the disk. What it cannot give you is a throughput figure: none has been measured yet. The parts of the running service Everything runs in one process: the Windows service hMailServer, or the systemd unit hmailserver on Linux. Part What it is Notes Mail listeners SMTP, POP3 and IMAP on IOService + TCPServer Boost.Asio HttpServer The engine under two listeners: REST (/api/v1, the webmail at /portal, the Control Deck) and web services (MTA-STS policy, autoconfig, ACME http-01) Its own io_context, so a request storm cannot starve SMTP accept. Since 6.2.28 Metrics listener /metrics and the health probes Raw sockets, one worker thread ManageSieve Sieve script management Raw sockets, a thread of its own SNMP agent Read-only, UDP 161 Since 6.3.4; off, and answering nobody until SnmpAllowedAddresses names a manager Background Scheduler, SMTPDeliveryManager, ExternalFetchManager Long-lived tasks on the main server queue State The database (accounts, domains, message metadata and settings, since 6.3.4 including the [Settings] keys), one file per message, and hMailServer.ini as the bootstrap The database stays small next to the store The handover that matters for capacity: an SMTP session never delivers anything. The final 250 is sent from a worker on the asynchronous task queue once the message is written, and delivery happens later on a delivery thread, so the accepting side and the delivering side run out separately. Diagnosing stalled mail takes each half in turn; How email works follows one message through both. The pools, their sizes, and what a full one looks like Seven work queues are created at startup, and the two HTTP listeners have workers of their own. The first column is the queue label of hmailserver_workqueue_depth{queue} on the metrics endpoint, which counts tasks accepted and not yet started; the last row is not a work queue and has no label. Queue Threads Sized by What runs on it When it is full IOCPQueue 15 tcpipthreads Every socket read, write and TLS handshake for SMTP, POP3 and IMAP New connections are accepted but nothing progresses; clients time out mid-command Asynchronous task queue 15 MaxNumberOfAsynchronousTasks Message finalization, which sends the final 250 Senders sit after CRLF.CRLF with no reply Name lookup queue 15 the same setting Blocking reverse-DNS lookups for live sessions Reverse names become "Unknown", so a dead reverse zone cannot stall acknowledgements SMTP delivery queue 10 maxdelivertythreads The delivery of one queued message The queue grows and mail is late; nothing is lost, since the sender already has its 250 External fetch queue 15 MaxNumberOfExternalFetchThreads One run per fetch account Fetch accounts fall behind their schedule Maintenance queue 5, fixed nothing One-shot scheduled tasks; a backup started over REST A slow sweep delays the other one-shot sweeps Server queue 4, fixed nothing Scheduler, IOService, SMTPDeliveryManager, ExternalFetchManager Cannot fill in normal operation: each task holds one slot for the life of the server HttpServer workers 4 per listener, fixed nothing One REST or web-services request each Requests queue behind the four; SMTP is unaffected Where the four sizes are set. On Windows, the Control Panel's Maintenance → Performance page, Threads tab, holds the COM Settings properties TCPIPThreads, MaxAsynchronousThreads and MaxDeliveryThreads, and the [Settings] key MaxNumberOfExternalFetchThreads. On either platform the first three are in the server settings group: the Control Deck's Settings view, PUT /api/v1/settings with tcpip_threads, max_asynchronous_threads and max_delivery_threads, or hmctl settings set. The fourth goes through /api/v1/settings/ini/{name} (administrator password only), hmctl ini set, or on Linux hmailserver --set-setting as root. Since 6.3.4 the database is the settings store, so do not edit the [Settings] line in hMailServer.ini: a line edited there is ignored in favour of the stored value. Settings: when a save takes effect covers the store, what becomes of the file's line (HM5804) and when each key applies. The tab also shows WorkerThreadPriority, read-only: the server stores that value and never reads it. When a new size takes effect. Since 6.3.4 a save applies most settings at once, but MaxNumberOfExternalFetchThreads waits for a reinitialise, because the queue's size is set when the fetch manager is built; GET /api/v1/server/settings/pending lists it until then. For the other three the documents leave a gap: the Control Panel reports a save of its COM settings as "applied immediately", while the Architecture page has the pools created when the servers start, and no document says when a changed count reaches a running pool. Until one does, treat all four alike: after saving, reinitialise (the Control Panel's Reinitialize or Apply now, POST /api/v1/server/reinitialize, or hmctl api POST /api/v1/server/reinitialize) or restart the service. A reinitialise stops and starts every service in place, ends the Control Deck's session, and ends a drain. # The REST listener on this machine: read the server settings group, # then give delivery 25 threads (or: hmctl settings set --set max_delivery_threads=25) curl -u Administrator:pw http://127.0.0.1:8443/api/v1/settings curl -u Administrator:pw -X PUT -H "Content-Type: application/json" \ -d '{"max_delivery_threads":25}' http://127.0.0.1:8443/api/v1/settings # The fetch pool is a [Settings] key; it waits for a reinitialise hmctl ini set MaxNumberOfExternalFetchThreads 20 hmctl api POST /api/v1/server/reinitialize Two asynchronous threads are kept back. Finalization runs the spam tests and event scripts, which wait on things outside the process, so AsyncQueueReservedThreads (2) keeps that many threads free of it: with the defaults at most 13 finalizations run at once. A task waiting for a slot holds no thread and is counted by hmailserver_workqueue_blocking_tasks_waiting{queue}; a count that stays above zero means something those tasks depend on is slow. On Windows the key is on Server limits & expert settings, card Timeouts and queue bounds; anywhere, hmctl ini set. Only two queues report a stall: the asynchronous and name lookup queues write the "every thread busy" report that Diagnosing stalled mail reads. The others are left out deliberately, since their tasks are long-lived by design, so a full IOCPQueue, SMTP delivery queue or External fetch queue shows only in hmailserver_workqueue_depth{queue} and in its symptoms. The database connections every pool shares The deliveries, the sign-ins and the rest of the server share one pool of database connections, NumberOfConnections in the [Database] section of hMailServer.ini, 5 as shipped. A wait for one longer than DBConnectionAcquireTimeout (60 seconds) fails. In hmailserver_db_connections{state}, available pinned at zero with busy at the pool size is saturation, and every mail path is then waiting out that timeout. Raise it with the delivery threads. The relay profile sets maxdelivertythreads to 25 and the sender profile to 50, and each tells you to raise NumberOfConnections to about the delivery thread count. The built-in database has one connection. On Windows, NumberOfConnections is forced to 1 for MSSQLCE. Before You Install calls the built-in engine adequate for anything up to a few dozen mailboxes, and its single connection the first thing to change if the server feels slow under concurrency. Linux has no built-in database; it uses PostgreSQL or MariaDB. Choosing a database covers the choice. It is a bootstrap key. [Database] stays in the file, never in the database, and its keys wait for a restart. On Windows the Performance page's Database tab edits it, on the server machine only. On Linux: sudoedit /etc/hmailserver/hMailServer.ini # [Database] NumberOfConnections=25 sudo systemctl restart hmailserver Connections and requests: the ceilings in front of the pools Where Limit As shipped Set on SMTP sessions MaxSMTPConnections 0, no cap Connections & protocols → Protocols, SMTP tab; max_smtp_connections IMAP sessions MaxIMAPConnections 0, no cap The IMAP tab; max_imap_connections POP3 sessions MaxPOP3Connections not given in the documents; GET /api/v1/settings shows it The POP3 tab; max_pop3_connections Recipients in one message 50,000 50,000 No setting is named; beyond it, 550 Too many recipients REST listener, with the webmail and the Deck 64 connections, closed at accept before any TLS handshake; 4 workers; 64 KB a request (16 MB, with 300 seconds, on the two attachment routes); 30 seconds a request; 300 seconds a connection fixed no key Web-services listener 128 connections; 4 workers; 64 KB a request; 15 seconds a request; 120 seconds a connection fixed no key The webmail shares the REST listener: its users, the Control Deck and every script that calls the API meet the same 64 connections and four workers. Its request rate is in the topic on the REST API. Outbound, since 6.3.4, MaxOutboundConnsPerDestination and MaxOutboundConnsPerTenant (both 0, off) cap sessions per destination and per sending domain; a tenant share larger than the delivery thread count is no share at all (Outbound shaping and IP warm-up). Scheduled work The Scheduler polls its recurring tasks once a minute; one-shot tasks go through the Maintenance queue at once. External fetch is not one of them: the ExternalFetchManager runs its own one-minute loop. Roadmap.md recorded in August 2026 that a due task runs on the scheduler's own thread, so one long task delays the others, and that the real period is the interval plus the run time. The tasks, grouped by cadence: Task Cadence Registered GreyListCleanerTask every GreylistingRecordExpirationInterval minutes (240 as shipped) when SMTP is on RemoveExpiredRecords, WorkQueueHealthTask every minute always; the second writes the stall report TlsRptReporterTask, DmarcRptReporterTask hourly always; each sends only when its from-address is set LogRetentionTask, MailboxRetentionTask start + 6 hours always ArchiveRetentionTask, IMAPExpungeRetentionTask start + 12 hours always MetricsHistoryTask start + every minute always UpdateCheckTask start + every 15 minutes always; does nothing until UpdateCheckEnabled=1 MessageStoreConsistencyTask, DiskSpaceMonitorTask start + hourly always BackupScheduleTask a one-minute tick against the wall clock only when a schedule is set DirectorySyncScheduleTask start + every [LDAP] SyncScheduleMinutes only when that is set AcmeRenewalTask start + hourly only when AcmeEnabled=1 Sizing the machine Memory. Before You Install asks for 2 GB as a minimum on Windows, more for a busy server. It is guidance; nothing enforces it, and memory per session has not been measured. Disk. Size it for how much mail you will keep, not how much you have today. The store is Data\ under the program folder on Windows and /var/lib/hmailserver on Linux, one file per message. For scale, the webmail bench's hundred thousand messages, 5 % of them with an attachment of 20 to 512 KB, are 1,323,987,827 bytes. Below MinimumFreeDiskSpaceMB (100) new mail is refused temporarily, 452 4.3.1 over SMTP and NO [UNAVAILABLE] to an IMAP APPEND, so senders retry; below DiskSpaceWarningThresholdMB (1024) the server logs a warning and keeps accepting. Antivirus. On Windows, exclude Data\, Database\ and Temp\ from real-time scanning. A scanner holding a message file open is a documented cause of delivery that silently does nothing; the server detects one shape of it and logs error 5760. Before You Install names no exclusions for Linux, where the store is /var/lib/hmailserver with its temp and database subdirectories. Durability. MessageStoreFsync (0 as shipped) flushes the spool file to disk before the 250, and a failed flush refuses the message. The published-numbers method runs with it on and labels the shipped default "not durable". Set it on Windows on Server limits & expert settings (card Message store durability), or anywhere with hmctl ini set. A folder of a hundred thousand messages in the webmail Every webmail path reads the folder's shared message collection, the one IMAP, POP3 and delivery use: one small object per message. Until 6.3.4 opening a folder copied the whole of it, under the lock IMAP sessions and delivery wait on, for every page of 200. Since 6.3.4: Operation What it does now What still grows with the folder Open, GET /api/v1/me/folders/{id}/messages One pass under the lock counts the folder and copies only the page of 200; each row's file head is read twice, not three times That pass; and the first open after a start, or after thirty idle minutes in the cache, reads every row of the folder from the database, as an IMAP SELECT does Scroll Older and Newer by before_uid; from three pages up, Oldest in one request (oldest_before_uid) Every page costs what the first costs, wherever it is in the folder: the one pass that counts the folder Search At most 2,000 message files and about two and a half seconds a request, then answers where to go on from; across every folder it walks the mailbox newest first and answers next_before_id; with IndexerFullText and message indexing on (off as shipped), a message the index rules out is passed without opening its file Performance.md does not say Select-all and one action The selection goes to POST /api/v1/me/folders/{id}/bulk: at most 1,000 messages a call for a flag, 500 for a move or a delete, nothing new taken on after about two and a half seconds A changed flag is a row written, a move is a file copied, and a selection narrowed to an inbox tab reads the head of every message Junk over a whole folder is a hundred thousand lessons. With SpamAssassin on, each message moved into or out of Junk queues one lesson, as the same move over IMAP does. In the Settings Reference the lesson on a move is SpamAssassinLearnOnMove (off as shipped), run as a task on the asynchronous work queue. A move holds the mail twice, briefly: original and copy, both counted against the quota, for no longer than one slice of the work. Not changed. The Storage, Subscriptions and Scheduled views still copy each folder they look at; emptying Junk or Trash copies the folder once per 500 messages removed; a quick step over a whole-folder selection still reads at most five thousand messages. For other callers of GET /api/v1/me/search, it now answers complete: false, more: true with next_before_id whenever it is not the end, and its hits come newest first across every folder, where they came folder by folder. Over IMAP, one SEARCH or SORT is bounded by IMAPSearchTimeout (60 seconds) and IMAPSearchMaxMegabytes (2048 MB examined). The webmail portal at /portal covers the rest of the client. What has not been measured The webmail at a hundred thousand. Performance.md has the method and a results table for the Windows bench (SQL Server Compact, 100,000 messages in the inbox, the full-text index off and on). Every cell reads to be measured on the bench; nothing was estimated. Its tools, build/perf/seed-folder.py and build/perf/webmail-timing.py, are in the repository and also run against a Linux server. Record the machine, database, message store, index state and build with any numbers. How much the server carries. PublishedNumbers.md defines messages in and out per second, latency percentiles, IMAP sessions per core with every operation's 95th percentile under 200 ms, memory per session and Dovecot's imaptest, measured against Postfix and Dovecot on the same machine under ten fairness rules. Its results say Not measured yet, and until they are filled the first line of Roadmap2.md is a goal, not a claim. The load generator, in the repository since 6.3.4, is build/perf/load/loadgen.py; a run in which the generator was busy for more than 85 per cent of it gives only a floor and is not published. The Windows bench gives hMailServer's own numbers only, not to be compared with the Linux table. python build/perf/load/loadgen.py --help python build/perf/load/loadgen.py self-test # proves the generator, says nothing about any server Size from the ceilings above, watch the queue depths and the database pool, and treat any throughput figure quoted elsewhere as unmeasured by the project. What it does not do No document gives a rule for sizing the pools to a machine; the only sized examples are the relay and sender profiles' 25 and 50 delivery threads. The main server queue, the maintenance queue and the HTTP listeners' workers, connection caps and deadlines are fixed; no key changes them. Where to read more Wiki: Architecture, Before You Install, Control Panel Reference section 7.8, Settings Reference, The REST API section 8, APIs Reference section 1 and Monitoring and Health. Documents: Performance.md, PublishedNumbers.md, SettingsApply.md, Profiles.md and OutboundShaping.md. The public documentation: Before you install, A database and Prometheus metrics and health probes. On this forum: Diagnosing stalled mail, Reading the logs, Monitoring and health, The webmail in depth, The Control Deck and hMailServer on Linux. Without a GitLab account, a bug or a question can go to the Service Desk address contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, which GitLab files as a confidential issue; the replies arrive by email.
  • 0 Votes
    1 Posts
    154 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) is a security release that closes gaps in what this guide describes. Over COM an account given serveradmin with a list of domains was the server's administrator, its list aside, and one given domainadmin with a list that left out its own domain administered that domain's ordinary accounts; both are now confined as over REST, so before upgrading give serveradmin with no list to any account that is to administer the whole server over COM, a script's or a Control Panel's account among them. A credential confined to named domains is refused a domain's rename, a domain alias, a custom role's creation, change or deletion, a rule action that reaches the server's own addresses, a route's stored credentials or the event script, a relay host that is not public, and the server-wide metric history, over REST and COM alike; and a role is given or taken away only by a credential that takes in what the account holds now and afterwards, so manage_roles in named domains can no longer take a role from an account whose admin level is the server administrator's. A state-changing REST request that a browser reports as cross-site is refused. In the Control Panel, API & monitoring is Monitoring & alerts and REST API keys is under Sign-in & access; Ctrl+K takes every old title to its page. See the 6.3.7 announcement. This guide puts administrator roles in one place. Roles are new since 6.3.4 (schema 6047). The one thing to know first: an upgrade changes nobody's authority. The upgrade writes no assignment, and an account with no assignment holds exactly what its admin level has always given it. Nothing narrows until you give somebody a role. Permissions, roles and assignments A permission is one thing a credential may do, a role is a named set of permissions, and an assignment gives one account one role in named domains. An account holds at most one role. Role For Reaches when no domain is named The same as serveradmin Everything, including the secrets no other role reads Every domain The administrator password, admin level Server, an API key with Scope=full and no role domainadmin The accounts, aliases, lists and mailboxes of a domain The account's own domain Admin level Domain helpdesk Finding an account, resetting its password, unlocking it, and reading where its mail went, and nothing else The account's own domain Nothing before 6.3.4 auditor Reading everything the administration exposes, the audit trail included, and changing nothing Every domain An API key with Scope=readonly and no role The built-in roles cannot be edited or deleted. Their permissions are in the server's code, not the hm_roles table, so a change to the table cannot hand anybody a permission. The 27 permissions and who holds them A permission marked server-wide is reached only by a credential with no list of domains. The queue, the quarantine, the archive, the logs and the settings carry every domain's mail, so a role confined to named domains is refused them whatever it holds. Permission What it reaches serveradmin domainadmin helpdesk auditor administer_server Everything, secrets such as password hashes and private keys included; COM's "is this the server administrator?" yes administer_domain COM's "is this a domain administrator?" yes yes view_configuration Status, settings, listeners, certificates, rules, routes, anti-spam lists, the roles and who holds them, alert rules and events, bounce and complaint readings; never a secret yes yes yes view_accounts Domains, accounts, aliases, lists, groups (server-wide), fetch accounts yes yes yes yes view_mailbox Another account's folders and messages yes yes yes view_queue The delivery queue (server-wide) yes yes view_quarantine The quarantine (server-wide) yes yes yes view_archive The archive (server-wide) yes yes view_logs The logs (server-wide) yes yes view_message_trace The message trace; with view_configuration, the reports and a domain's suppression list yes yes yes yes view_audit The audit trail (server-wide) yes yes manage_accounts Creating, changing and deleting accounts, aliases, lists, groups, fetch accounts and app passwords; a domain's spam traps, DKIM rotation and suppression list; moving a mailbox (needs both domains) yes yes reset_password POST /api/v1/accounts/{address}/password, Application.ResetPassword yes yes yes unlock_account POST /api/v1/accounts/{address}/unlock, Application.UnlockAccount, Account.UnlockMailbox; clearing the logon-failure list (server-wide) yes yes yes manage_mailbox Another account's folders, messages and folder rights, a support session, a PST migration job (with manage_accounts) yes yes manage_queue Retrying or deleting a queued message (server-wide) yes manage_quarantine Releasing or deleting held mail (server-wide) yes yes manage_archive Holding and releasing archived messages (server-wide) yes manage_settings Every setting, listener, certificate, route, rule and anti-spam list, the alert rules (server-wide); the webmail's branding and file-link policy, a confined credential only its own domains' yes manage_domains Which domains exist (server-wide) yes manage_server Reinitialise, back up, reload scripts, clear caches, install an update, run a pass now (server-wide) yes manage_roles Roles and who holds them (server-wide) yes send_through_api Sending through POST /api/v1/send; only an API key reaches it yes provision SCIM 2.0 at /scim/v2, reads and writes; only an API key reaches it yes view_discovery eDiscovery's cases, searches, holds and exports yes yes manage_discovery Opening and closing a case, searching, legal holds, exports yes manage_inbound_webhooks Routing an address or a domain to a URL, as much as reading every message the route takes yes Four rules sit beside the table: The auditor reads mail. It holds view_mailbox; the help desk does not. The reports need two permissions. GET /api/v1/reports needs view_configuration and view_message_trace, because it says who mails whom. domainadmin and auditor read them; helpdesk is refused, and the Deck leaves the Reports view out for any role without both. The trace follows the role's reach. A confined help desk traces one whole address in a domain it reaches and sees only events naming that address exactly: [email protected] does not also show [email protected]. Clearing the logon-failure list is server-wide. hm_logon_failures records only the address a failure came from, with no account or domain, so a confined help desk is refused POST /api/v1/settings/logon-failures/clear, and Settings.ClearLogonFailureList answers the server administrator alone. The help desk unlocks one account with POST /api/v1/accounts/{address}/unlock instead; a password reset clears the lockout too. A ban already placed is an IP range under /api/v1/ipranges, which needs manage_settings. What only serveradmin has Reading a secret. No view_ permission covers a password hash or a private key: an auditor that could read one would be an offline attack away from being every role. Handing out authority. No custom role may hold administer_server, administer_domain or manage_roles, and a credential may never give a role holding a permission, or reaching a domain, that it does not hold itself. Taking over a wider account. A credential may not reset the password of, or change, an account whose whole authority it does not take in. A help desk is refused a domain administrator's or an auditor's password, even in its own domain, because otherwise it could sign in as them; a domain administrator is refused an auditor's. Where the server administrator's own mailbox is in a domain somebody else administers, that domain's administrator is refused all of these on it, over REST and COM. Over COM this is asked where the write is made, not where the object was reached: Account.Save, Account.Delete, Account.MoveTo, Application.ResetPassword, the app passwords and every write to what is in an account (FetchAccount.Save, Rule.Save, IMAPFolder.Save, Message.Save and the rest) ask that the credential reach the account and take in its authority. An event script's objects carry no credential and are left as they were. Roles.md lists every route and COM member that asks this. Which domains a role reaches Credential No domains named * A list An assignment of serveradmin or auditor Every domain Every domain Only those An assignment of any other role, custom ones included The account's own domain Every domain Only those An API key's Domains= line, whatever its role Every domain Matches no domain Only those A custom role holding a server-wide permission needs * before an account can use it at all. The listings answer every_domain over REST, and a fourth field over COM. A credential confined to named domains sees only the assignments of accounts in those domains. Giving a role, surface by surface Control Panel (Windows only): Accounts & domains → Administrator roles lists the roles and who holds them. Type an address, choose a role, optionally name the domains, and press Give the role; select a row and press Take the role away. It applies at once. Control Deck (Windows and Linux): the Roles view does the same. REST: giving or taking away a role needs manage_roles, which only serveradmin holds and which is server-wide: the administrator password, or a serveradmin credential reaching every domain. PUT /api/v1/role-assignments/[email protected] {"role":"helpdesk","domains":"example.test,example.org"} DELETE /api/v1/role-assignments/[email protected] GET /api/v1/role-assignments GET /api/v1/roles COM (Windows only): an empty role name takes the role away, and Account.RoleName, RoleDomains and RolePermissions read it back: $app = New-Object -ComObject 'hMailServer.Application' $account = $app.Authenticate('Administrator', 'your-admin-password') if (-not $account) { throw 'Authentication failed.' } $app.AssignRole('[email protected]', 'helpdesk', 'example.test') $helen = $app.Domains.ItemByName('example.test').Accounts.ItemByAddress('[email protected]') $helen.RoleName; $helen.RoleDomains; $helen.RolePermissions With a second factor on the administrator, Authenticate refuses and the script uses AuthenticateWithCode. Application.CurrentRole and CurrentPermissions say what a session holds. An API key: POST /api/v1/apikeys with "role":"helpdesk" writes a Role= line to hMailServerApiKeys.ini. The key store answers to the administrator password alone. The Deck's API keys view, signed in with that password, also makes a key with a role and its domains, showing the token once. curl -u Administrator:your-admin-password \ -H "Content-Type: application/json" \ -d '{"label":"help desk tool","role":"helpdesk","domains":"example.test"}' \ https://<host>:8045/api/v1/apikeys Use the address and port your listener answers on; bound to loopback with no certificate, it is http://127.0.0.1:8045 (https:// once a certificate, or an ACME one, is in place). A key's role decides what it reaches. The Control Panel's REST API keys page, as its reference describes it, names no role; give a key its role over REST or in the Deck. No API key signs in to the Deck. Custom roles are made over REST from a name, a description and a comma-separated permission list; both editors list them and give them out. A role that releases held mail holds server-wide permissions, so it is given with * and reaches every domain's held mail: POST /api/v1/roles {"name":"quarantine-desk","description":"releases held mail","permissions":"view_quarantine,manage_quarantine"} PUT /api/v1/role-assignments/[email protected] {"role":"quarantine-desk","domains":"*"} A permission the server does not know is dropped, not refused, so a custom role ends with less authority than it asked for, never more. A custom role somebody holds cannot be deleted: DELETE /api/v1/roles/{name} answers 409 saying how many accounts hold it, and the Deck's Delete shows that sentence. Give each holder another role or take this one away first; assignments are never removed for you, because an account with none falls back to its admin level. A built-in role answers 400, and COM cannot delete a role. An assignment decides even when its role holds nothing: holders of a custom role whose permissions name nothing this version knows are ordinary accounts, not their admin level. Where each role works Role Control Deck REST API Control Panel (COM) serveradmin Every view Every route Every page domainadmin Dashboard, DANE / TLSA, domains, fetch accounts, lists, roles and reports for its domains Its domains' accounts, aliases, lists and mailboxes, the status, who holds a role there Domains and Administrator roles helpdesk Domains, fetch accounts and lists, with Reset password and Unlock on every account The reset, the unlock, the account listings, the trace of an address in its domains Welcome and About only; Application.ResetPassword, UnlockAccount and GlobalObjects.MessageTrace answer a script auditor Every view that reads, the audit trail and reports included Every read, the audit trail and alerts included; every write refused Welcome, About and Administrator roles The help desk and the auditor work in the Deck. COM's settings objects answer only the server administrator, so an auditor reads the configuration through the Deck or REST. A domain administrator is not shown the settings, rules, routes, remote domains, certificates, ports, IP ranges, groups, backup, server messages, queue, logs or audit trail, which are server-wide. The quarantine is server-wide as well, so a domain administrator confined to its domain is refused it. Hiding a page is a courtesy, not the boundary. The server refuses the request whatever a page draws, and a Control Panel connected to a server that predates roles shows every page. A role holder signs in with its own address and password, and its second factor where it has one. An app password never carries the role: over REST it is an ordinary account's request, and COM's Application.Authenticate and AuthenticateWithCode treat it as a wrong password. A support session into a mailbox does not carry the person's role either. A role holder's own mail and settings under /api/v1/me stay its own. Setting up a help desk Turn the REST listener on if it is off. It needs RestApiPort set, RestApiBindAddress set to an address staff can reach (the default, 127.0.0.1, serves this machine alone), a certificate unless it is bound to loopback, and a name staff can reach. On Windows set them on the Control Panel's API & monitoring page (Monitoring & alerts since 6.3.7) and press Apply now, since they wait for a reinitialise. On Linux run sudo hmailserver --set-setting RestApiPort=8045 and the same for RestApiBindAddress and, for any address but loopback, RestApiCertificateFile and RestApiPrivateKeyFile (or let an ACME certificate stand in), then sudo systemctl restart hmailserver. The Control Deck topic has the details. Give each person a mailbox of their own; the role goes to that account. Decide the domains. With none named, helpdesk reaches only the account's own domain; name several, comma separated, for a desk serving more. Give the role on any surface above and check it with GET /api/v1/role-assignments or the Roles view. The person signs in to the Deck at https://<host>:8045/ as themselves, then uses Reset password or Unlock on an account. Tell them the limits: no mail reading, no reports, no domain administrator's or auditor's password, no removing a ban, and, when confined, no clearing of the logon-failure list. Each change the help desk makes is on the audit trail as its own, over COM and REST, with actor type account and a detail ending [role: helpdesk]. What a refusal says A permission the role lacks: 403 naming both, for example the role 'helpdesk' does not hold the permission 'manage_settings'. A domain it does not reach: the role 'helpdesk' does not reach that domain, whether or not the account exists, so nobody can list another domain's mailboxes one request at a time. Over COM, Application.ResetPassword and UnlockAccount answer No such account. for both. An account wider than the credential: that account holds authority this credential does not. Over COM, since 6.3.4 a refusal carries the server's sentence rather than a bare Exception from HRESULT: 0x800403E9. An API key on the key store, the [Settings] routes, the audit trail or the alerts: 401 with {"error":"authentication failed"}, the same as "not a key". The two stores only the administrator password opens The key store (/api/v1/apikeys) and the [Settings] section of hMailServer.ini (/api/v1/settings/ini) answer to the administrator password and nothing else: not an account holding serveradmin, not a key of any role or scope. A key that could mint keys could issue itself an unrestricted replacement, and the file holds the server's secrets. An account is refused 403 with the reason, a key 401. The audit trail (/api/v1/audit) and the alerts (/api/v1/alerts/...) are refused to every API key with the same 401, so a stolen key cannot read what it did or silence the alerting. An account whose role reaches every domain reaches them through view_audit, view_configuration, manage_settings or manage_server; a confined role is refused them. Upgrades, the audit trail and backups The upgrade. Schema step 6047 adds hm_roles, seeded with the four built-in rows, and hm_roleassignments, and writes no assignment. Roles are data, not settings: nothing goes into hm_settings or hMailServer.ini. The audit trail. Every role created, changed or deleted, and every role given or taken away, is a row naming the credential that did it; an assignment's row reads like role: helpdesk -> auditor; domains: the account's own domain -> every domain. The actor type follows the role: the administrator password and holders of serveradmin or domainadmin are administrator, every other account is account. Backup and restore. An account's assignment travels with it, by role name, and the custom roles travel whenever the domains or the settings do, restored first. A role the restoring server already has is kept as it is, with any difference named in the backup log. The admin level waits until the assignment is written, so a restore that stops part-way leaves no account wider than its role. An assignment naming a role neither side has is dropped, the level stays withheld, and HM6129 names the account. No archive carries the API key store. What it does not do No mapping from an identity provider. There is no claims mapping; an identity provider saying somebody is an administrator is not obeyed. The server's roles and oidc_administrators or saml_administrators decide. No role on an app password or a support session. No Control Panel or COM on Linux: roles are given there in the Deck and over REST. Where to read more Roles.md, the authority on all of this. Sign-in and Identity §7, The Control Deck, The REST API §2 and The COM API and Scripting §23.2. AlertsAndAuditTrail.md, Backup.md and Reports.md. On this forum: The Control Deck: browser administration at /, The REST API: turning it on, credentials and roles, The COM API on Windows, Hardening a public mail server, Alerts and the audit trail and IP ranges, auto-ban and lockout.
  • 0 Votes
    1 Posts
    117 Views
    ProgressiverobotP
    This topic covers how much mail an hMailServer 6.3.4 server keeps, and for how long: each mailbox's limit and the quota policy 6.3.4 adds around it; retention, the administrator's and each person's, with the recover-deleted window; and the server archive under ArchiveDir. The one thing to know first: out of the box nothing is deleted because of its age. A new account has no limit (0), no domain has a retention policy, the archive is off, and once it is on, ArchiveRetentionDays is 0, keep for ever. The limit, and what a full mailbox does Where it is set. Account.MaxSize over COM, max_size_mb over REST, in megabytes; 0, the default, is no limit. Set it on the Control Panel's account dialog (Quota (MB), Windows only), in the Control Deck's account editor, with PUT /api/v1/accounts/{address}, in the max_size_mb column of an accounts CSV file (since 6.3.4), or with Set-HmAccount. A domain's Limits tab caps the whole domain (Maximum domain size (MB), 0 for none; once reached, delivery to its mailboxes fails) and sets the quota new accounts get. What counts is each message's original size: compression does not lower it, and a mailbox encrypted at rest keeps its sizes in the clear. The kept copy of a message the person sent to expire counts against their quota (413, quota_exceeded). Where At the limit SMTP RCPT TO, with RejectFullMailboxAtRcpt on (1, the default) A mailbox already at or over its limit, and past any grace (below), is answered 452 4.2.2 Mailbox is full., a temporary refusal: the sending server keeps the message and retries A message that arrives while there is room and would overflow it Caught at delivery, with a non-delivery report to the envelope sender RejectFullMailboxAtRcpt at 0 Accepted, then bounced to an envelope sender that spam has usually forged. Leave it on IMAP APPEND, COPY, MOVE Refused with [OVERQUOTA], so a client can tell a full mailbox from any other refusal APPEND into a folder shared from another mailbox Since 6.3.4 charged to the folder's owner, as COPY and MOVE were A migration or PST import job Pauses with the account has no room left under its quota and tries again; raise the limit or make room A full disk is a different refusal. Below MinimumFreeDiskSpaceMB (100) free on the store's volume, MAIL FROM gets 452 4.3.1 Insufficient system storage. and IMAP APPEND NO [UNAVAILABLE], for every mailbox. The log warns first, below DiskSpaceWarningThresholdMB (1024). What the person sees. The webmail's Storage page (since 6.3.2, GET /api/v1/me/storage the share used, every folder by size, the twenty largest messages, and a clean-up of Trash and Junk older than 7, 30 or 90 days, or everything. See The webmail portal at /portal. GET /api/v1/me answers quota with limit_mb and used_bytes. IMAP QUOTA: QUOTA=RES-STORAGE is advertised; GETQUOTA and GETQUOTAROOT answer one unnamed root for the whole account with STORAGE in kilobytes, and an empty list for an account with no limit. SETQUOTA is refused: the limit is the administrator's. It is on as seeded; the switch is imap_quota_enabled on PUT /api/v1/settings (IMAPQuotaEnabled over COM). The quota policy (since 6.3.4) The server-wide warning at QuotaWarningPercent (90) has been there since 6.2.22-pre6. Schema 6061 adds a domain's own warning share and words, a grace and a send-stop, each a share of the limit; schema 6088 lets an account hold its own shares too. Point Server Domain (-1 takes the server's) What it does Warning QuotaWarningPercent, 90; 0 warns nobody quota_warning_percent The delivery that takes the mailbox across this share sends its owner one notice, from the domain's postmaster Grace quota_grace_percent, 0 quota_grace_percent How far past the limit mail from other people is still accepted Send-stop quota_send_stop_percent, 0 (never) quota_send_stop_percent The share at which the account can no longer send Over COM (Windows): Settings.QuotaGracePercent, Settings.QuotaSendStopPercent, and Domain properties of the same names. The warning is sent once, by the delivery that crosses the threshold; nothing records who was warned, so a mailbox emptied and filled again is warned again. The grace is for mail other people send, the mail otherwise lost. A 1 GB mailbox with a 50% grace receives until it holds 1.5 GB; the owner's own saves and copies over IMAP and the webmail stop at the limit. The send-stop refuses the account, not the address. An authenticated MAIL FROM is refused with 552 5.2.2 and the reason, keyed on the account that signed in, even under a domain alias, not the address it named. Receiving goes on. QuotaPolicy.md describes the refusal at SMTP only; test the webmail's send and the send API before relying on it there. The words are the QUOTA_WARNING_SUBJECT and QUOTA_WARNING server messages, with %MACRO_MAILBOX%, %MACRO_QUOTA% (MB), %MACRO_USED% (MB) and %MACRO_PERCENT%. Server messages are server-wide (the Control Panel's Mail flow & delivery → Server messages, the Deck's Server messages page, PUT /api/v1/settings/messages/{name}). A domain sets its own subject and text, quota_warning_subject and quota_warning_text (Domain.QuotaWarningSubject, Domain.QuotaWarningText), so its people can be warned in their own language. They are chosen apart: a domain that sets only the text keeps the server's subject. Levels. The warning is a preference: the deepest level holding a value wins. The grace and the send-stop are limits: an account's own value may tighten its domain's, never loosen it, while a domain's may sit either side of the server's. The domain's shares are the domain administrator's, and a domain-restricted key's. <name>_effective and <name>_source on the domain and account routes say why a mailbox was not warned. An account's own quota shares are tested only through what the routes answer, and only on Windows. The mechanism is in Settings: what is not stock, when a save takes effect, and the three levels. Where it is set. Control Deck: the domain editor's Quota policy group; the server's grace and send-stop on the Settings page. Control Panel (Windows): the domain dialog's Limits tab; the server's grace and send-stop under SMTP, beside QuotaWarningPercent. REST: the five domain keys on PUT /api/v1/domains/{name}; the server's two on PUT /api/v1/settings. An account's own value: the Levels tab of the Control Panel's account dialog, the Deck account editor's levels card, hmctl account set, or PUT /api/v1/accounts/{address}. QuotaWarningPercent is a key of the settings store: set it in the Control Panel (Windows) on Connections & protocols → Protocols, SMTP tab, with PUT /api/v1/settings/ini/{name} or hmctl ini set, which answer to the administrator password alone (never an API key, nor an account holding a role), or, on the Linux server itself, hmailserver --set-setting, not by editing hMailServer.ini. Every value here applies when it is saved through the server, at every level; when a save takes effect explains why a line edited in the file does not hold and when a value stored by --set-setting arrives. A backup taken before the policy restores with the server's values. One domain warned at 85%, stopped from sending at the limit and given a 20% grace, with a German subject; then one account put back on its domain's grace: hmctl domain set example.com quota_warning_percent 85 hmctl domain set example.com quota_send_stop_percent 100 hmctl domain set example.com quota_grace_percent 20 hmctl domain update example.com --set "quota_warning_subject=Ihr Postfach ist fast voll" hmctl account set [email protected] quota_grace_percent inherit Who is nearest their limit GET /api/v1/reports/quota lists every mailbox with a limit and at least one message, nearest first: megabytes, quota_megabytes, percent, the warns_at and sending_stops_at points that apply to it, and a state of ok, warned, stopped or over. It takes domain, top (1 to 200, default 10) and format=csv, and ignores the date window. Its test has run on the Windows bench only. The Deck's Reports page shows it as Near their quota. Other than the administrator password, a credential needs view_configuration and view_message_trace. The sections hmctl report documents do not include it: curl -u Administrator:your-admin-password \ "http://127.0.0.1:8443/api/v1/reports/quota?domain=example.com&top=200&format=csv" Nothing records what a mailbox held yesterday; per-domain storage by day is the storage section, in Alerts, the audit trail, syslog and the journal, and per-domain reports. Retention: the administrator's policy MessageRetentionDays (since 6.2.25) is set per domain and per account; there is no server value. On a domain, 0 (the default) is no policy and a number deletes mail older than that many days. On an account, 0 (the default) takes the domain's, a number is its own, and -1 keeps for ever whatever the domain says. The sweep. MailboxRetentionTask runs at start-up and every six hours. It removes delivered messages whose stored creation time is older than the window, in every folder of the mailbox, through the same deletion an IMAP EXPUNGE performs, so the quota, the QRESYNC record and an open IMAP session all see it. Utilities.RunMessageRetention (COM, Windows only) runs it now and returns the count; no document names a REST route or hmctl verb for it, so on Linux wait for the next pass. Where it is set. On Windows the Control Panel's domain dialog (Limits tab) and account dialog (General tab); the Deck's domain and account editors; message_retention_days on the domain and account routes; hmctl domain set and hmctl account set. A domain's retention is the server administrator's (COM refuses a domain administrator, the domain route a restricted key); an account's own value is the domain administrator's, so an account's -1 can switch its domain's policy off for that account. Before switching a domain's policy on: Give -1 to each account that must keep everything. Decide whether what the sweep removes should be recoverable for a while (recover_deleted_days, below). Off, a removed message is gone. Set the domain's days. It applies when saved; the next pass, at most six hours away, deletes. Easy to miss. A restore of the built-in backup does not carry a domain's retention days: the wiki's Backup and Restore page lists them as silently reset to 0, so re-enter them. And a legal hold outranks retention: a held account is left alone by the policy and by the person's own choices, and the log says so. Each person's own retention (since 6.3.4) Three choices on the webmail's preferences page: Empty Trash after, Empty Junk after and Archive Inbox mail after, each never, 7, 30 or 90 days, or a year, kept as retention_trash_days, retention_junk_days and retention_archive_days. The same six-hourly sweep runs them after the administrator's policy. Trash and Junk (the folders designated \Trash and \Junk, or named as clients name them) are emptied of what is older; Inbox mail older than the choice is copied to the \Archive folder, made as a top-level Archive if there is none, then removed from the Inbox. That is a folder of the person's mailbox, not the server archive. A value that is not a whole number of days from 1 to 3650 is off. The recover-deleted window (since 6.3.4) recover_deleted_days on PUT /api/v1/settings or the Deck's Settings view (0 to 365; 0, off, by default) keeps a copy of what a person deletes, and of what either retention removes, for the person to put back from the webmail's Recover deleted items. A domain or an account may keep copies for fewer days than the level above, never more. The copies, under <data directory>/Recoverable/<account id>/, count against no quota and are not in the backup, so size the window with the disk in mind; the six-hourly task ends those past the window, and all of them at 0. The person's side is in The webmail in depth: search, offline mail, expiring messages, send-as and recovery. The server archive With ArchiveDir set, the server writes a copy of every message received by SMTP into a tree of its own, as received and in the clear: encryption at rest does not cover it, and the mailbox retention policy does not reach it. The layout, as the Settings Reference writes it (Windows separators, names lower-cased): Message Where the copy goes From a local sender ArchiveDir\<senderdomain>\<sendername>\Sent-<file> From anybody else ArchiveDir\Inbound\<file> For each local recipient ArchiveDir\<recipientdomain>\<recipientname>\<file>, made from the first copy when there is one A bounce or report with a null sender The archive's Error folder, with no index row The archive's settings: Key Default What it does ArchiveDir empty (off) The folder the copies go to ArchiveDomains empty (every message) Comma-separated: a message is archived only when its local sender or a recipient is in a listed domain, and only the listed domains' copies are made ArchiveHardLinks 0 Hard-link instead of copy: a promise that nothing outside the server edits a message file in place ArchiveRetentionDays 0 (keep for ever) Deletes archived copies older than this many days Its sweep runs at start-up and every twelve hours. It removes only .eml files, follows no symbolic link or junction, is depth-capped, and asks the index first: a held copy stays however old. The index (since 6.2.25, schema 6029): a row per copy in hm_archiveindex, with who, when, what, where and whether it is held; copies made before schema 6029 have none. Search it in the Deck's Archive view (since 6.3.4), with GET /api/v1/archive (domain, mailbox, sender, recipient, subject, since, until, hold=1, limit 1 to 1000, default 200, newest first), or with Utilities.SearchArchive over COM (Windows). A role needs view_archive; a key restricted to named domains must name one of its own, and the Inbound copies are the administrator's alone. Searching the words of archived mail is an eDiscovery case's work. A hold on one copy, POST /api/v1/archive/{id}/hold or Utilities.SetArchiveHold, keeps its file and row from the sweep and from an address's erasure; DELETE lifts it. Holds on whole accounts and domains are eDiscovery's: see Compliance: eDiscovery, legal holds, personal data, erasure and the audit trail. curl -u Administrator:your-admin-password \ "http://127.0.0.1:8443/api/v1/archive?domain=example.com&[email protected]&since=2026-09-01&limit=50" curl -u Administrator:your-admin-password -X POST http://127.0.0.1:8443/api/v1/archive/4471/hold Where it is set. On Windows, the Control Panel's Maintenance → Advanced, Copies of mail tab (ArchiveDir, ArchiveHardLinks, ArchiveDomains), and ArchiveRetentionDays on the SMTP tab of Connections & protocols → Protocols. The Deck has no page for these keys: use PUT /api/v1/settings/ini/{name} or hmctl ini set <name> <value> (the administrator password only), which take effect when saved, or hmailserver --set-setting NAME=VALUE (with sudo) on the Linux server itself, which, while the service runs, waits for the next save, a reinitialise (POST /api/v1/server/reinitialize) or a restart. Setting one up: Choose a folder outside the data directory: a full restore with the messages option replaces that directory, and an archive inside it too, held copies included. On Windows, a new installation runs the service as NT SERVICE\hMailServer, which writes only where it is granted: approve the folder by running hMailServer.exe /GrantServiceAccess with --approve-modify "<folder>" from an elevated Command Prompt in the installation's Bin folder. It must be a full path on a fixed local drive, outside the program folder, Program Files, the Windows folder and the profiles folder: never a share, a drive's root, or a path through a junction or link. On Linux the service runs as User=hmailserver, and the packaged unit lets it write only under /var/lib/hmailserver, the data directory itself, and /var/log/hmailserver. Every other folder is read-only to it (/home is not even visible), and an ArchiveDir it cannot write stops archiving. Make the folder writable by the hmailserver user, add it to the unit with a drop-in (sudo systemctl edit hmailserver, then under [Service] a ReadWritePaths= line naming the folder) and restart the service. SecureDefaults.md gives this drop-in for a data folder; no document gives it for the archive. Set ArchiveDomains first if only some domains are archived, then ArchiveDir. Send a test message and find its row in the Deck's Archive view. Before relying on it. The backup does not carry it. The backup copies the data directory, and hm_archiveindex is not carried: back up the archive folder, and the database with its own tool for the index. Moving it strands the index. hm_archiveindex.archivepath is matched as an exact string by the sweep, holds and erasure; do not rewrite it in bulk. If the folder moves, move it back or accept that older rows point at the old place. A mailbox move leaves the archive under the old address, and an address's erasure with the archive included leaves the copies in other people's trees and in Inbound until ArchiveRetentionDays ages them out. It is not journaling, which sends a copy elsewhere and keeps nothing here: Journaling and dual delivery. SMTP, as documented. No 6.3.4 document says whether mail the server queues itself, such as the webmail's send, or mail a fetch account collects, is archived. What it does not do No message-count quota: storage is the only quota enforced. No server-wide retention policy, and no documented way to run the retention sweep on demand on Linux. No write-once archive: a hold is a flag an administrator can lift. The archive only grows while ArchiveRetentionDays is 0, and the recover-deleted store is bounded by its window alone: watch the disk they are on. Where to read more Documents at v6.3.4: QuotaPolicy.md, SettingLevels.md, Reports.md, RecoverDeleted.md, Discovery.md, Backup.md, ServiceAccount.md, RelocatingAnInstallation.md, PersonalData.md, Journaling.md, SecureDefaults.md, SettingsApply.md. Wiki: Settings Reference (section 9.13), Your First Domain and Mailbox, Compliance and eDiscovery, Backup and Restore, APIs Reference. The documentation page: Storage, retention and archive, Queue, quarantine and archive. On this forum: Backup and restore, for what a restore resets; Relocating an installation, and moving or renaming a mailbox, for the archive index; the service account on Windows and the Linux unit, for the folders the service may write; The Control Deck.
  • 0 Votes
    1 Posts
    369 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.6 (6 October 2026) adds three one-way schema steps (6106 to 6108), changes more without a switch (DANE lookups that do not come back whole, external accounts' days to keep messages, the COM helpers, Windows folder permissions, the REST listener's Host) and installs by the live update from 6.3.4 or 6.3.5: read the 6.3.6 announcement before upgrading. hMailServer 6.3.7 (17 October 2026), a security release, adds no schema step (6108 stays, so the live update's rollback from it to 6.3.6 meets no newer schema) and installs by the live update from 6.3.4, 6.3.5 or 6.3.6, with the exceptions its announcement lists; it too changes what an upgraded server does without a switch: an authenticated submission is DKIM-signed as another hosted domain only when the account may send as the From address; a domain's or an account's rule may no longer set From, Sender, Resent-From, Resent-Sender, DKIM-Signature or the loop count, nor a Sieve script add or delete them or send notify from an arbitrary :from; RuleLoopLimit 0 stops out-of-office replies; the serveradmin role with a list of domains is confined to those domains over COM; forwards of outside mail take an envelope sender in the hosted domain only on a DMARC pass; the anti-virus action that removes attachments replaces a single-part attachment message with its note and deletes unreadable mail; SASL PLAIN acting as another account is refused; on Windows /ProtectFolders hardens the program folder, Bin and ControlPanel, the x64 installer's MariaDB Connector/C 3.4.11 drops MySQL Enterprise's Windows authentication, and an upgraded server keeps the .NET 10 Desktop Runtime it had, where the installers carry 10.0.13. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release, adds no schema step either (6108 stays, so the live update's rollback from it meets no newer schema) and installs by the live update from 6.3.4, 6.3.5, 6.3.6 or 6.3.7, with the exceptions its announcement lists; of what it changes without a switch, these are the most likely to need something done before upgrading: TlsMode=verify-full on MySQL or MariaDB without TlsCaFile, to a database server on the same machine, is refused with HM7535; on Windows a file whose path holds characters outside ASCII - the database's TlsCaFile or PostgreSQLSslRootCert, a TLS certificate or private key file, dh2048.pem, a client-certificate CA file, MessageStoreObjectCaFile, OAuth2PublicKeyFile or AcmeCertificateDirectory - may be refused with HM7534 or HM7536; on Linux files are named in UTF-8 whatever LANG says, so where a server ran under a LANG that is not UTF-8, each file or folder named outside ASCII is to be renamed to its UTF-8 name; a sign-in name spelt otherwise than the stored address, in more than the case of the letters A to Z, or with white space at either end, may no longer sign in; an IP range of your own that allows some addresses none of SMTP, POP3 and IMAP now closes the web services listener to them too; a REST body that is not one JSON object within the bounds is answered 400; an AcmeDomains host the certificate lacks is ordered at the first check, and one the CA cannot validate holds the renewal for the others; and on Windows setup refuses with exit code 7 a program folder, Bin or ControlPanel, or a folder or a link on the way to them, that another account controls, and refuses an installation of the original hMailServer's 5.7, which no upgrade step reaches: move such a server's mail over IMAP instead. See the 6.3.8 announcement. This topic covers what comes after the installer in an upgrade to 6.3.4 (27 September 2026, schema 6104): the 24 changes CHANGELOG.md lists under "What changes on upgrade without a switch", with what to do about each; what else differs coming from 5.x or early 6.x; what the server says when the schema and the server do not match; and how to go back. The path, the slow steps and the backup are in Upgrading to 6.3.4. Know this first: an upgrade cannot undo itself. An older server refuses a newer schema rather than misread it, so the database backup taken before the installer ran is the only way back. Going back from 6.3.4 can also need the hMailServer.ini you had before the upgrade, as Rolling back from 6.3.4 below explains. Keep a copy of both. The 24 changes, and what to do about each Numbered as CHANGELOG.md numbers them; all are since 6.3.4. # What changes What to do Detail 1 The database is the settings store (schema 6042); [Settings] in hMailServer.ini is its copy. A line edited in the file is put back and named as HM5804 Do not edit the file to prepare the upgrade; restart the old service once after your last change. Afterwards use the Control Panel, COM (Settings.SetIniSetting), REST (PUT /api/v1/settings/ini/{name}) or, on Linux, hmailserver --set-setting NAME=VALUE Upgrading to 6.3.4; Settings 2 211 keys apply when saved, 28 at a reinitialise, 2 at a restart A script that counted on nothing happening until the next start now sees its change at once Settings: when a save takes effect 3 SPF follows RFC 7208: 127.0.0.0/8 and ::1 no longer pass before any policy is read. SPF now works on Linux, where every check had been TempError A content filter that re-injects mail authenticates, or connects from an IP range with anti-spam off The anti-spam stack 4 The audit trail is on (schema 6043), kept until a retention is set; alerts are on, but nothing is mailed until AlertRecipient and AlertSenderAddress are set Set AuditRetentionDays, and the two addresses Alerts and the audit trail 5 Compromised-account detection starts in watch mode (schema 6071): it raises account.compromised and holds nothing Set compromise_detection_mode to enforce when you want a takeover held Hardening: compromised-account detection 6 A 421, or a provider's rate-limit 4xx, pauses that destination for OutboundBackoffMinutes (2, doubling to 60) Nothing; 0 turns it off Outbound shaping 7 A backup carries what an account keeps beyond its mail: address book, preferences, calendars, S/MIME keys, keywords and more Expect larger archives. One from 6.3.3 or earlier restores as it did, without them Backup and restore 8 The out-of-office memory is a table (schema 6060) Nothing: a restart no longer sends everyone the reply again Rules and Sieve: vacation replies 9 Collected addresses are suggestions under Other contacts; CardDAV serves only kept contacts Tell users a phone no longer receives collected addresses; Keep makes one a contact Calendars and contacts 10 In the container image, HM_SETTING_<key> and HM_PROPERTY_<name> are stored at every start, and the setting is refused to the other interfaces Remove the variable and start once without it. No container image of 6.3.4 is published In a container 11 GET /api/v1/me/search goes on from where it stopped Below Below 12 IMAP answers as the RFCs say: NO rather than BAD for a missing mailbox, \Seen set by every BODY[<section>] without .PEEK, SORT and THREAD ordering, APPEND byte for byte Test clients and scripts that relied on the old answers Connecting a mail client 13 MySQL and MariaDB tables become utf8mb4 (schema 6102), every table rebuilt MySQL 5.7.9 or MariaDB 10.2.2 or later, the ALTER privilege on the database, free disk of the order of the largest table Upgrading to 6.3.4; Databases on Linux: the upgrade floor 14 Schema 6101 adds two columns to hm_messages On a large store, time it on a copy first Upgrading to 6.3.4 15 The database connection is encrypted when the database server offers TLS, for MySQL and MariaDB without checking the certificate; PostgreSQL is unchanged Read the application log at the first start, or run --check-database; TlsMode and TlsCaFile in [Database] choose otherwise Choosing a database: encrypting the link 16 The administrator password is hashed again at the first successful administrator sign-in; a browser session made under the old hash ends once Sign in once. A file the service cannot write keeps the old value (HM7494) Hardening a public mail server 17 A COM change to a route, an incoming relay, a DNS black list, an SSL certificate or five more kinds of shared object takes effect at Save() Add Save() where a script relied on setting a property; the Control Panel is not affected The COM API on Windows: Save() 18 Mail labelled ISO-8859-1 is decoded as windows-1252 Below Below 19 A restore refuses an unsafe archive whole (HM7540) Nothing: no backup this server writes has such an entry Backup and restore 20 A rule's Run function action must name a plain function: otherwise HM5018 on Windows, and on Linux nothing runs and the ERROR log has a Script Error line Rename such functions to ASCII letters, digits, _ and $, not starting with a digit Event scripts: the 6.3.4 security fix; the advisory 21 A domain can no longer be named as one of the server's own directories Below Below 22 A one-time code is taken once, over COM and REST: a TOTP code the server has accepted is refused if it comes again within its window, but not counted as a wrong code, so it locks nobody out A script that sent the administrator's password and a code with every REST request now has only its first request in each half minute accepted, the others refused with 401: sign it in once with POST /api/v1/session and use the hmailsession cookie, or give it an API key The REST API: one-time codes 23 The outbound relay sends its OAuth2 token over TLS only: a route or the SMTP relay whose host is on OutboundOAuth2Hosts and whose connection security is none, or STARTTLS optional against a server that does not offer it, fails with a temporary 450 4.7.0 (HM5912), and the message waits for the route's retries Set that connection security to TLS or STARTTLS required. A route to Exchange Online on 587, set up as Microsoft documents it, is not affected Routes, relays and smart hosts: OAuth2 24 A hash stored before 6.3.4 of a password with a character above U+00FF is refused Set such passwords again, and soon Upgrading to 6.3.4; Hardening a public mail server Three that no table row can hold ISO-8859 (18). ISO-8859-1 is decoded as windows-1252, as every mail client reads it, and ISO-8859-2 to -9, -13 and -15 by their own code pages. There was no ISO-8859 name in the server's table at all, so a Western Windows server decoded Latin-1 right by accident and decodes it as before, one with another ANSI code page now decodes it right too, and on Linux Latin-1 is no longer turned into replacement characters. There is nothing to set. Reserved domain names (21). A domain's mail is kept in the directory named after it under the data directory, beside the server's own stores. So a single-label name such as protected, confidential, recoverable, discovery, quarantine, files, lists or blobs, in any case, or temp, events and database, which the Linux packages put there, is refused for a new domain and for a rename over COM (Domain.Save) and REST, and so in the Deck. The Control Panel already refused every name without a dot. A domain that already has such a name is still saved, so that it can be managed, and is named as HM7597 in the error log at every start and as domain_name_reserved on the first-hour checklist. Rename it: the rename moves each of its accounts' mailboxes and leaves the store's files where they are. Until then, a domain named recoverable, discovery, confidential or protected has its mail in a folder the backup leaves out. The search route (11). Only a client of GET /api/v1/me/search other than the webmail needs to change. A search with more hits than limit answered complete: true, more: true and could not reach the rest; it now answers complete: false, more: true with next_before_id, which before_id continues from. Hits come newest first across every folder by message id, where they came folder by folder (Performance.md). Coming from 5.x or early 6.x The 24 apply to you too, and so do the changes of every release in between: section 5 of Changes Since 6.2.10 lists what came without a switch from 6.2.10 to 6.2.28. On 5.3 to 5.5, back up first and test the restore. Four things are particular to an older installation, all on Windows: The Administrator is replaced by the Control Panel. hMailAdmin.exe was retired in 6.2. The Control Panel, hMailCP.exe, does everything it did, and its navigation tree mirrors the old layout. Your TOTP secret carries over. The PHP WebAdmin is gone. Administer a remote server with the Control Panel, which connects to it directly, or from a browser with the Control Deck. The "Administrative tools" component now only registers the COM type library for scripts on another machine. A pre-5 hMailServer.ini moves into Bin. The installer copies it from the Windows directory to Bin\hMailServer.ini, leaves a .old copy where it was and deletes the original; the server reads only the one in Bin. The .NET 10 runtime. Since the first release after 6.2.18 the Control Panel and the setup tools (DBSetup, DBSetupQuick, DBUpdater) need the .NET 10 Desktop Runtime. The installer bundles windowsdesktop-runtime-10.0-win-x64.exe and installs it /quiet /norestart when it is missing (6.3.7's installers carry 10.0.13 and install it only where no .NET 10 Desktop Runtime is installed at all: an upgraded server keeps the one it had, 10.0.12 where 6.3.4 to 6.3.6 installed it, and brings it to 10.0.13 or later through Microsoft Update or Microsoft's installer). If the runtime does not install. Upgrading.md says the server, native code with no .NET dependency, is left working without the Control Panel. The documentation's 18.5 says otherwise, and for an upgrade to 6.3.4, which always moves the schema, it is right: Upgrading.md itself says the setup tools need the runtime too, and the server refuses to start against an old schema, so there is no mail server until the schema is moved. Install the runtime (x64) by hand from https://dotnet.microsoft.com/download/dotnet/10.0, run DBUpdater.exe from the installation's Bin folder, then start the service. When it goes wrong At every start the server compares the version in hm_dbversion with the one it requires and refuses to run if they differ, in either direction. The ERROR log has one of these: Code and message What it means What to do HM5011: The database is too old for this version of hMailServer. Please run hMailServer Database updater (DBUpdater.exe) to upgrade it. The schema did not get all the way up Run DBUpdater.exe from the installation's Bin folder HM5011: The database is too new for this version of hMailServer. Please upgrade hMailServer. An older server against a newer schema: an old binary left in place, or a previous release reinstalled Install the matching or a newer server. Do not try to downgrade the database HM5010: Database version could not be detected. hm_dbversion could not be read at all Check the connection details in [Database] and that the login can read the table Both HM5011 messages end with Database version: <n>, Required database version: <m>. On Windows the service reports itself running before this check, so a refused start leaves a service Windows calls Running with nothing listening: read the ERROR log. A 6.3.3 server against a 6.3.4 database is the too-new case on purpose: step 6041 to 6042 changes only the version, the fence that keeps a 6.3.3 server, which lets hMailServer.INI win, away from a database that is now the settings store. Resuming. DBUpdater picks up from whichever version the database actually reports, so a retry after a partial upgrade resumes rather than restarting. A failed step leaves PostgreSQL exactly as it was and SQL Server normally so; MySQL, MariaDB and SQL Server Compact commit DDL as it executes and keep the steps that ran. The same chain also runs: on Windows, since 6.3.4, as hMailServer.exe --upgrade-database with the service stopped, without DBUpdater's window or the administrator password; it exits 0 (done, or nothing to do), 1 (failed; the output names the step) or 2 (no [Database] configured); on Linux as hmailserver --upgrade-database, which the .deb and .rpm run after installing; at the service's own start with UpgradeOnStart=1 in [Database] (since 6.3.4, off by default): a failing step refuses the start with HM5011 naming the step, and the next start resumes there. It backs nothing up, and belongs on one node at a time (Warm standby). DBUpdater exits 0 on success, 1 when the upgrade failed, 2 on a configuration error, and 3 when authentication was cancelled or, under /silent, could not be satisfied: see HM_ADMIN_PASSWORD in Unattended installs. The installer fails with a non-zero exit code of its own; treat that, or a schema version still on the old value, as the failure: select value from hm_dbversion; Asking for help. Give the versions you started from, the step DBUpdater stopped on and the database's own error number; Read this before you post has the rest. Without a GitLab account, email the Service Desk address, contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com: GitLab files it as a confidential issue, and replies come by email. Rolling back from 6.3.4 Uninstall 6.3.4. Restore the pre-upgrade database backup. This is the essential step: the schema is at 6104, and the release you go back to refuses it. Put the right hMailServer.ini in place before the older installer runs, because its /Register reads the file. Which file depends on whether 6.3.4 sealed the stored secrets, that is, whether a stored-secrets key was made: Sealed: restore the pre-upgrade hMailServer.ini. 6.3.4 writes the [Settings] secrets into the file as HMK1: envelopes, and an older server reads them as they are: its /Register hands ServiceAccountPassword's envelope to Windows as the service account's password, and the service cannot log on. Its first moments also run SRS, BATV and OAuth2 on the envelopes. Not sealed: the 6.3.4 file can stay. Its [Settings] section is a complete copy of the stored settings, and an older server reads the file as the truth, so a setting changed after the upgrade survives, wherever the server could write the file, which a Linux package installation's unit does not allow. Delete the [SettingsStore] section, which older builds ignore. They ignore [SettingsOverride] too, so move any value only it supplied into [Settings]. Check PasswordPepper. Since 6.3.4 it is kept in the file only, never sealed and in no backup 6.3.4 writes. The file the older server starts with must carry the value the hashes were made with: no pepper, or a different one, refuses every account hashed with Argon2id or scrypt. PBKDF2, the default, is not peppered. Install the release you came from. Sealed, with no pre-upgrade file? Set ServiceAccountPassword, and any other of those secrets you use, again with the older version's Control Panel, then run hMailServer.exe /Register again. The wiki's and the documentation's 18.6 reinstall before they restore the database; Upgrading.md restores first. Follow Upgrading.md: the older installer runs DBUpdater, which refuses a database newer than its own build, and its /Register reads the file. The data directory normally stays. Upgrading.md and the wiki's 18.6 say it does not normally need restoring, because no upgrade step rewrites message files; the documentation's 18.6 says to restore it with hMailServer.INI. Left in place, mail that arrived after the backup exists on disk with no metadata row in the restored database, and is invisible. That is what a rollback costs. What it does not do The rollback above is the Windows one. Chapter 18 and Upgrading.md give no package command for going back on Linux; the database rule is the same. No backup carries a [SettingsOverride] value; it lives only in the file of the machine that has it. Moving to another machine is not an upgrade: Moving to new hardware. Where to read more Upgrading hMailServer, 18.5 and 18.6; the documentation's 18.6 Upgrading.md and its wiki copy, Upgrading Guide CHANGELOG.md, 6.3.4, and Changes Since 6.2.10, section 5 SecretsAtRest.md, "Rolling back", and Settings Overview SetupChecklist.md and AccountRecovery.md, for items 21 and 22 Troubleshooting, for a start that fails
  • 0 Votes
    1 Posts
    157 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.6 (6 October 2026) is signed by the GitLab pipeline at the group path and installs by the live update from 6.3.4 or 6.3.5; from 6.3.6 the Control Panel installs an update with one click where the service is not an administrator, and on Linux a 6.3.4 or 6.3.5 server installs 6.3.6 with its package manager rather than by the live update, whose apply step on those versions is the one 6.3.6 fixes. See the 6.3.6 announcement. hMailServer 6.3.7 (17 October 2026), a security release signed at the same group path, was what the feed offered before 6.3.8: from 6.3.6 the live update installs it, with 6.3.6's file as its rollback image and no schema step between them; on Windows a service running as LocalSystem installs it itself, and under NT SERVICE\hMailServer an administrator presses Install update on the Control Panel's Server status page on the server, one Windows prompt; on Linux 6.3.6's own helper applies it to the generic packages and both AppImages, while a server from a per-distribution package upgrades with its package manager. From 6.3.4 or 6.3.5 what 6.3.6's notes said still holds, and a server whose UpdateSigningIdentity, UpdateSigningIssuer and UpdateSourceRepository still name the project's first path refuses 6.3.7 until the three are cleared, on 6.3.6 by deleting their lines from [Settings] in hMailServer.ini with an editor run as an administrator (on Linux sudo hmailserver --set-setting <name>=) and restarting the service. In 6.3.7's Control Panel, Updates is a page of its own under Maintenance. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release signed at the same group path, with the project's id, 86730559, in the certificate, is what the feed now offers: from 6.3.7 or 6.3.6 the live update installs it, with the running version's own file as its rollback image and no schema step between them, a service running as LocalSystem by itself and one under NT SERVICE\hMailServer by Install update on the server; from 6.3.4 or 6.3.5 what 6.3.7's notes said still holds; a server from one of 6.3.6's or 6.3.7's per-distribution packages is offered the generic package, which apt refuses on Debian 12 and dnf on Enterprise Linux 9, so upgrade it with its release's own package of 6.3.8; the three settings that name the project's first path refuse 6.3.8 too until they are cleared; and a server from 6.2.28 to 6.3.3, or from 6.3.3's AArch64 packages or AppImages, installs 6.3.8 by hand, once. See the 6.3.8 announcement. This topic covers the update check and the live update as 6.3.4 has them. The one thing to know first: no live update installs 6.3.4. It carries an Authenticode signature and no Sigstore bundle, so a server of any version that is offered it says it cannot be verified and downloads nothing. Every server installs 6.3.4 by hand, as Upgrading to 6.3.4 describes. A server from 6.2.28 to 6.3.3 has a second reason to do it soon: its live update will refuse 6.3.5, the first release to be signed on GitLab. Four steps, and where each is done Each step is separate and opt-in, and only applying runs anything. Step Control Panel (Windows) REST COM (Windows) Check Check for updates on Server status POST /api/v1/update/check Status.CheckForUpdate Download and verify Download update POST /api/v1/update/download Status.DownloadUpdate Apply Install update POST /api/v1/update/install Status.InstallUpdate Read the verdict the Server card of Server status GET /api/v1/update Status.UpdateState, Status.AvailableVersion, Status.UpdateLastError, Status.UpdateApplyOutcome The Control Deck's Update view does all four on either platform; on Linux there is no Control Panel and no COM. The state is 0 not checked, 1 up to date, 2 a newer release exists, 3 downloaded and verified, 4 installing, or 5 the last step failed (lastError says why). An on-demand check reads the feed whether or not the scheduled check is on. Starting an install from the Control Panel closes it, because the installer replaces its files. Turning the check on UpdateCheckEnabled is 0 by default, because a server must not call out to anyone until its administrator says it may. At 1 the server reads the feed at startup and then every UpdateCheckHours (24; below 1 is taken as 1, and the server sets no ceiling although the Control Panel's label says "1 to 168"). UpdateChannel is stable by default; prerelease makes pre-releases eligible. What it sends. A plain GET of the feed with nothing but the User-Agent the server always sends: no identifier, no configuration, no counts. Since 6.3.4 it no longer sends GitHub's X-GitHub-Api-Version header. Where the result shows. The application log, the Control Panel's Server status page, the Deck's Update view, COM, and GET /api/v1/update, which adds the release's page and date, the installer's address, size and digest, and the last apply's outcome. Setting the keys since 6.3.4. Use the Updates card of the Control Panel's API & monitoring page (in 6.3.7 Updates is a page of its own under Maintenance, and Ctrl+K takes the old title to it), PUT /api/v1/settings/ini/{name} with {"value": "..."} (administrator password only), hmctl ini set, or on Linux hmailserver --set-setting; the Deck has no editor for these keys. Every key here applies when it is saved, but a value stored with --set-setting while the service runs reaches it only at its next save, reinitialise or restart. Do not edit the [Settings] lines in hMailServer.ini: the database is the settings store, and Settings: when a save takes effect explains what happens to a line edited in the file. curl -u Administrator:pw -X PUT -H "Content-Type: application/json" -d '{"value":"1"}' http://127.0.0.1:8443/api/v1/settings/ini/UpdateCheckEnabled curl -u Administrator:pw -X POST http://127.0.0.1:8443/api/v1/update/check The feed Since 6.3.4 a server reads https://updates.progressiverobot.com without being told: /releases/latest, the newest stable release, on the stable channel, and /releases.json, every release, on the pre-release channel. GitLab Pages serves it from the project's releases. For the rollback the server looks one release up at /releases/tags/v<version>. Every address a server builds is a file answered 200, and the server follows no redirect. A release has the shape of GitHub's Releases API, and each installer's Sigstore bundle is an asset of its own, named after its file with .cosign.bundle added. A download is held to the size and the digest the feed gives. Since 6.3.4, a version with a suffix (6.3.5-rc1, 6.3.5rc1) is a pre-release whatever the feed's prerelease flag says, so the stable channel passes over it. A mirror. UpdateFeedUrl points the server at another feed, for a network without Internet access; empty is the built-in feed. It must be https unless it is on this machine. It may serve one release or a list, and the rollback's lookup keeps everything up to /releases of the value and adds /tags/v<version>, so the mirror answers that path too. A mirror changes where the server looks, not whom it trusts: every installer is still verified against the signers compiled in. Through a proxy. HttpProxy=host:port (or [ipv6]:port) sends every web request the server makes as a client through a forward proxy: the feed, the installer and its bundle, and the JWKS and token-introspection fetches of OAuth 2. An https address is tunnelled with CONNECT, and the certificate is verified against the real host. There is no place for credentials; a proxy that demands them refuses. A refusal reads The proxy <host:port> refused CONNECT to <host:port>: <its status line>, and a value without a port fails with HttpProxy must be host:port (or [ipv6]:port)., never quietly going direct. ACME and the outbound-relay OAuth2 token request do not use it. Download and verification Download update, POST /api/v1/update/download, Status.DownloadUpdate or UpdateAutoDownload=1 fetches the installer and its bundle into the data directory's Updates folder (Data\Updates on Windows; on Linux the file is the .deb or .rpm this server was installed from) and checks them without cosign and without the network: the file's SHA-256 is the one signed and the one the transparency log recorded; the certificate chains to Sigstore's Fulcio authority at the moment the log recorded the signature; the certificate is one of this project's signers, whole; the signature verifies under the certificate's key, and the log vouches for the entry under its own key. A file that fails is deleted, and the reason is in Status.UpdateLastError and the log. Nothing is run. The two signers, since 6.3.4. A certificate must match one entry whole. The GitHub workflow The GitLab CI pipeline Signs Every release from 6.2.19 to 6.3.3 The releases from 6.3.5 on: 6.3.5 at the path the project began at, 6.3.6 and later at the group path hmailserver/hmailserver Identity begins https://github.com/Progressiverobot/hmailserver/.github/workflows/sign-release.yml@ https://gitlab.com/Progressiverobot/hmailserver//.gitlab-ci.yml@refs/tags/v for 6.3.5; from 6.3.6, https://gitlab.com/hmailserver/hmailserver//.gitlab-ci.yml@refs/tags/v, the path the project moved to on 29 September 2026 Issuer https://token.actions.githubusercontent.com https://gitlab.com Repository id 1267388815, required 86730559, checked when the certificate carries one Held to Certificates issued before 18 September 2026, and releases up to 6.3.3 The tag the certificate names The pipeline is also trusted at the path of the group hmailserver the project is to move to, where the id 86730559 is required whatever any setting says. A server from 6.2.28 to 6.3.3 trusts the GitHub workflow alone. Further checks since 6.3.4. A file signed for one tag is refused as any other release. On Windows the installer's version resource must name the release it is installed as; on Linux the update helper installs nothing that is not an upgrade. On Windows only SYSTEM, the Administrators and the service's account may change the Updates folder; one that is a link, owned by anyone else or beyond repair is refused with HM6894, and nothing is downloaded or applied: delete it, and the server makes it again. Authenticode. UpdateRequireAuthenticode=1 adds, on Windows only, a check that the installer carries an Authenticode signature Windows trusts (no revocation check, no publisher pin). The installers have been signed since 6.3.1; 1 refuses earlier ones. It is on top of the Sigstore check, which cannot be turned off. The trust settings UpdateTrustRootsFile, UpdateLogPublicKeyFile, UpdateSigningIdentity, UpdateSigningIssuer and UpdateSourceRepository are for a site that signs its own releases. Leave them empty; set, the last three replace their part of every signer. Applying an update On Windows, Install update, POST /api/v1/update/install, Status.InstallUpdate or the unattended window does this, in order: Verifies the downloaded installer once more. Fetches and verifies the running version's installer as a rollback image, where the feed still offers it. Issues a single-use token for the database upgrade: 32 random bytes in a file only the machine's administrators can read, good for one hour and one use, given to the installer as /upgradetoken=<hex> in place of the administrator password. Drains, since 6.3.4: the SMTP, IMAP, POP3 and ManageSieve listeners close at the socket, so senders retry, and the queue pauses. It refuses with already draining while somebody else's drain is in progress: leave it, or wait, and apply again. Hands the installer to hMailServer.Updater.exe, copied from Bin into Data\Updates because the installer replaces Bin. The helper runs it silently, and only with the SHA-256 the server read; waits UpdateServiceWaitSeconds (180) for the service; and if it does not come back, reinstalls the previous version. Reports the outcome: one line, in the log, Status.UpdateApplyOutcome, the status page and GET /api/v1/update; since 6.3.4, as soon as the service is back. If the helper finishes without stopping the service, the server leaves the drain itself after ten minutes at most (HM7151 when the helper wrote nothing); the helper's log is apply-<version>.log in the Updates folder. Set ShutdownDrainSeconds (30 is a reasonable figure) so the stop waits for what is in flight. What a rollback cannot do. It reinstalls the previous binaries; it cannot take the database back. When the new version had moved the schema and then failed to start, the previous version refuses the newer schema, and the outcome says so. Every release's notes say whether it moves the schema; before one that does, take a backup (Backup and restore). The rollback image is verified like any installer, and 6.3.4's has no bundle. UpdateFeed.md does not say what a 6.3.4 server's first live update does about that, so plan it as one without a rollback image, and take a backup first. In the one case UpdateFeed.md describes, an old server set to the GitLab signer (below), the install runs without a rollback image, the log says no rollback image ... could be had, and a new version that does not start has to be reinstalled by hand. Under NT SERVICE\hMailServer, the account a new 6.3.4 installation on Windows runs as, the apply is refused before anything happens, before the rollback image and the drain: the installer needs an administrator. The update is still checked, downloaded and verified. REST, COM and the Deck say so, and tell an administrator to download the installer from the release page, check its Authenticode signature (Get-AuthenticodeSignature reports it Valid) and run it; never the copy in Data\Updates, which the service can write. An open unattended window logs HM7463 once per process instead of taking a backup for nothing, and the setup checklist carries update_apply_by_administrator while the check is on. An upgraded installation that kept LocalSystem applies as before. Installing on Windows covers the account. On Linux, since 6.3.4, the service writes a request to /run/hmailserver and the root path unit hmailserver-update.path runs /usr/lib/hmailserver/hmailserver-update, which verifies the package and the rollback image again as root, installs, and rolls back when the service does not come back. No token is involved: the package's post-install step runs the schema upgrade. A server not under systemd is told so and left with the verified file to install by hand. The hmailserver command line on Linux has the helper in full. Linux builds from 6.3.0 to 6.3.3 answer This build cannot apply an update. Unattended updates Key Default What it does UpdateAutoDownload 0 1 downloads and verifies a newer release's installer as soon as a check finds it UpdateWindow empty, never When a verified update may be applied without a click UpdateBackupBeforeApply 1 An unattended apply backs up to the configured backup destination first, and does not go ahead without a destination or when the backup fails UpdateWindow takes 03:00 (every day from 03:00, for an hour), Sun 03:00 or Sat,Sun 02:00-05:00: English three-letter days, local 24-hour times, and a range that crosses midnight belongs to the day it starts on. An unparsable value is logged once per process and nothing is applied. The backup covers the unattended path only, so take one yourself before an apply you start. Under NT SERVICE\hMailServer a window applies nothing (HM7463). On Linux: sudo hmailserver --set-setting UpdateCheckEnabled=1 sudo hmailserver --set-setting UpdateAutoDownload=1 sudo hmailserver --set-setting "UpdateWindow=Sun 03:00" sudo systemctl restart hmailserver A server from 6.2.28 to 6.3.3 These versions read GitHub's Releases API, which has answered 404 since GitHub suspended the project's account on 18 September 2026, so their check says The update feed answered HTTP 404. Nothing else breaks. Pointed at the new feed, such a server trusts the re-hosted releases up to 6.3.3, which carry GitHub's bundles, but its check now offers 6.3.8, the newest stable release, signed by the GitLab pipeline, which it cannot verify either: its live update installs nothing until 6.3.4 or later is installed by hand. It will also refuse 6.3.5 (The certificate names https://gitlab.com/Progressiverobot/hmailserver//.gitlab-ci.yml@refs/tags/v..., not this repository's release workflow.). These versions read [Settings] from the file, and a change needs a service restart. Turn automatic download off. With UpdateAutoDownload=1 such a server would download a GitLab-signed installer, about 80 MB, at every check and refuse it every time, because the verification comes after the download. Set it to 0. Point it at the feed, with exactly this line under [Settings] in hMailServer.ini, then restart the service: [Settings] UpdateFeedUrl=https://updates.progressiverobot.com/releases.json Without .json the address is a directory, answered with a redirect, and the check says The update feed answered HTTP 302. On Windows set it on the Updates card and let the Control Panel restart the service; or add it to Bin\hMailServer.INI as an administrator and run net stop hMailServer and net start hMailServer from an elevated prompt. On Linux: sudoedit /etc/hmailserver/hMailServer.ini # add the line under [Settings] sudo systemctl restart hmailserver An AppImage keeps it in $HMAILSERVER_HOME/hMailServer.ini, or else $XDG_DATA_HOME/hmailserver/hMailServer.ini, normally ~/.local/share/hmailserver/hMailServer.ini; start it again after the edit. Check. The answer should be that the newest release, 6.3.8, is available. Still 404 means the service has not read the line: check the section, the spelling and the restart. The update feed could not be read: ... names what stopped it: no route out, a proxy (HttpProxy), or a certificate the machine does not trust. Install 6.3.4 by hand, following Upgrading to 6.3.4, with the backup Upgrading.md describes. The service must have been restarted after your last [Settings] change, or 6.3.4's first start treats that change as an edit (HM5804) and undoes it. 6.3.4 and 6.3.5 had no AppImage and no AArch64 package; 6.3.6, 6.3.7 and 6.3.8 have both again, and such an installation takes the newest release's, 6.3.8's, by hand, once. Afterwards, 6.3.4 reads the new feed without being told and trusts both signers. The UpdateFeedUrl line can go, although leaving it changes nothing: clear it in the Control Panel, with DELETE /api/v1/settings/ini/UpdateFeedUrl, or on Linux with hmailserver --set-setting UpdateFeedUrl=, not in the file. Turn UpdateAutoDownload back on if you want it. The other way, Windows only. UpdateFeed.md gives values for UpdateSigningIdentity, UpdateSigningIssuer and UpdateSourceRepository that let such a server install a GitLab-signed release. While set, they make it refuse every GitHub-signed release, its own rollback image included; they are proved only once a GitLab-signed release exists; and they must stay set until the new version runs, then be cleared as step 5 clears UpdateFeedUrl, or the new version refuses what is signed after the project's move. Never point them at the group's path: an old server checks no repository id. Installing 6.3.4 by hand avoids all of this. The download links, and checking a file by hand https://updates.progressiverobot.com/download/<name> always leads to a file of the newest stable release, never a pre-release, and /download/ to that release's page. GitLab's own /-/releases/permalink/latest follows the release published last, a pre-release included. A server does not read these links. <name> is a file's name without its version: hMailServer-x64.exe, hMailServer-arm64.exe, hmailserver_amd64.deb, hmailserver_arm64.deb, hmailserver.x86_64.rpm, hmailserver.aarch64.rpm, hMailServer-x86_64.AppImage and hMailServer-aarch64.AppImage, each with its bundle at the same name plus .cosign.bundle. A name the release does not carry answers 404, never another release's file. 6.3.4 carries the x64 installer, the amd64 .deb and the x86-64 .rpm, and no bundle. Checking 6.3.4. With no bundle to check, 6.3.4 is checked by hmailserver-6.3.4-SHA256SUMS.txt, whose signature hmailserver-6.3.4-SHA256SUMS.txt.sig is made with the key that signs the release tags, and on Windows by the installer's Authenticode signature, which Get-AuthenticodeSignature should answer Valid, signed by Progressive Robot Ltd. The 6.3.4 announcement has the commands and what each must answer. Other releases. Up to 6.3.3, cosign verify-blob checks a file against its bundle and the GitHub identity (Verifying a release in the Security Policy). From 6.3.5 a release has no .sig; its files are signed with Sigstore, and on Linux hmailserver --verify-update <file> checks a download the way the server does. What it does not do It does not install 6.3.4, and does not apply under NT SERVICE\hMailServer. A rollback does not take the database back; a backup does. It follows no redirect and sends no proxy credentials. It does not drain a warm-standby pair: the drain is per process, so a pair drains one node at a time (Warm standby and high availability). Where to read more UpdateFeed.md, ServiceAccount.md, Upgrading.md, SettingsApply.md, and the Update check section of the README. On the wiki: Upgrading hMailServer, Settings Reference, The REST API, Security Policy and Installing on Linux. The documentation page: Staying up to date and Self-update. On the forum: Upgrading to 6.3.4; Upgrading from 5.x, 6.2 or 6.3, for what 6.3.4 changes and rolling back by hand; and The Control Deck. Without a GitLab account, a question or a bug can go to the project's Service Desk, contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com. A suspected vulnerability goes only by the routes of SECURITY.md: a confidential GitLab issue or that same address, which GitLab files as a confidential issue.
  • 0 Votes
    1 Posts
    136 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.8 (2 November 2026), a security release, reads on Windows the CA file MessageStoreObjectCaFile names from the file configured, or refuses its name: a name it cannot hand to OpenSSL as it is named, as a path holding characters outside ASCII may be, is refused with HM7536 in the error log, and then no request to the object store is allowed, so give the file, and the folders above it, an ASCII name before upgrading. See the 6.3.8 announcement. This topic covers the three things 6.3.4 can do to a stored message that 6.3.3 could not: store it compressed with its large attachments held once, keep it in an S3-compatible or Azure Blob store instead of under the data directory, and store it encrypted to a key of the mailbox's own. All three are new in 6.3.4, and all three are off until an administrator turns them on: a server that turns none of them on stores every message exactly as 6.3.3 did. The one thing to know first: the delivery queue is never compressed, encrypted or put in the store. A queued message is plain RFC 822 on the local disk, so the event scripts, the virus scanners, SpamAssassin, the sandbox and the filter hook read what they always read. One door, and what a client sees Every place that reads, sizes, copies, moves or deletes a stored message now goes through one seam, MessageStore. Everything that hands a message out reads the original bytes back through it, so a client sees nothing different: IMAP FETCH BODY[], RFC822.SIZE, BODYSTRUCTURE, SEARCH, SORT and THREAD; POP3 RETR, TOP, LIST and STAT; the webmail and the REST API; the rules, Sieve, the quota (which counts the original sizes), and a DKIM signature checked against the stored copy. What changes is the file itself. Message.Filename over COM still answers the stored file's path. With compression on, a COM client or a script that opens that path reads a container, not RFC 822; with an object store, once the message is committed there is no file there. Read the message through the API instead. Compression and attachments held once Schema 6098. With MessageStoreCompression on, a message delivered to a mailbox, or put there by IMAP APPEND, is stored as a container: the message compressed with deflate, and each large MIME part, an attachment of at least MessageStoreDedupMinKB once decoded, cut out and held once as a blob that every message carrying the same bytes shares. A mail sent to forty people on this server with a 10 MB presentation keeps the presentation once. Where What Control Panel (Windows) Settings > Maintenance > Performance, the Message store tab Control Deck The two settings on the server settings page, and a Message store section on the dashboard REST message_store_compression and message_store_dedup_min_kb in /api/v1/settings; GET /api/v1/store; POST /api/v1/store/measure COM (Windows) Settings.MessageStoreCompression, Settings.MessageStoreDedupMinKB, Settings.MessageStoreStatus Both are rows of hm_settings and take effect for the next message stored. MessageStoreDedupMinKB is 256 unless changed, 0 to 65536, and 0 holds nothing once and compresses only. The switch rewrites nothing. The reader never asks the setting what a file is: it reads either form, per file, by the file's first eight bytes. Turning it off leaves every stored message readable. Only delivery and IMAP APPEND write the new form. A webmail draft, a message saved through COM, the importers and the IMAP mirror write plain. A message larger than 64 MB is stored as it came, because a container is decoded whole in memory. Every container is checked before a byte is served: its structure, each blob against its hash, and the SHA-256 of the whole. A damaged one is refused and named in the error log (HM7350), never served as something else. Two messages share a blob when the part's encoded bytes are identical. The same file attached twice by a client that wraps its base64 differently is two blobs. The command line, run as the user the server runs as (hMailServer.exe takes the same switches on Windows): hmailserver --measure-store [--account <address> | --domain <name>] hmailserver --compress-store [--account <address> | --domain <name>] [--decompress] hmailserver --recount-store Verb What it does Server --measure-store Changes nothing; prints what the store takes now, what it would take, and how much more it would save May be running --compress-store Rewrites every stored file in place, each written beside itself, read back and compared byte for byte, then renamed over its name Stopped --compress-store --decompress Puts the files back as plain RFC 822 Stopped --recount-store Sets every blob's count to what the stored files reference Stopped Exit 0 is a finished walk, 1 a file that could not be read or written, 2 no database or no such account or domain, and 4 a running server. How much it saves has not been measured on a real mailbox. MessageStore.md holds a placeholder where the figures are to go, and the wiki claims none. Run --measure-store on your own store and read the answer. The document also records that its regression fixture had not yet run against a bench when it was written. Object storage: S3 and Azure Blob The messages in the mailboxes, the blobs and the recover-deleted store's kept copies can be kept in an S3-compatible store (AWS S3, MinIO, Wasabi, Backblaze B2's S3 API, Ceph RGW), signed with AWS Signature Version 4, or in Azure Blob Storage, with Shared Key or a SAS. The database stays the index, and an object is named by the path its file had. There is no schema step. Nothing changes until a store is named. Every write is a local one. A message is placed on the local disk exactly as without a store, and a committer puts it into the store afterwards, and only then removes the local file. It runs every minute; what it has been handed is journalled in <data directory>\_ObjectStore\pending.txt and taken up again after a restart. So a delivery never waits for the store and never fails for it, and every read looks at the local disk first. Property REST key Default MessageStoreBackend message_store_backend file; the other value is object MessageStoreObjectService message_store_object_service empty; s3 or azure MessageStoreObjectEndpoint message_store_object_endpoint empty, which is the service's own MessageStoreObjectRegion message_store_object_region us-east-1 MessageStoreObjectBucket message_store_object_bucket empty MessageStoreObjectAddressing message_store_object_addressing auto; also path, virtual MessageStoreObjectPrefix message_store_object_prefix empty MessageStoreObjectAccessKey message_store_object_access_key empty MessageStoreObjectSecret message_store_object_secret empty MessageStoreObjectSasToken message_store_object_sas_token empty MessageStoreObjectCaFile message_store_object_ca_file empty MessageStoreObjectCacheMB message_store_object_cache_mb 32 These are rows of hm_settings, set over /api/v1/settings or on the Control Deck's Settings > Server form. There is no COM member and no Control Panel page for them. The secret and the SAS are protected, never emitted, and left out of a backup, so after a restore onto another server they are entered again. TLS verification is always on, and no setting turns it off. With MessageStoreObjectCaFile set, the certificate is verified against that file: beside the system's bundle on Linux, and alone on Windows. Turning it on: Make the bucket or container. This server makes none. For S3, an access key allowed s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket and the multipart upload calls, and a lifecycle rule that aborts incomplete multipart uploads after a day. Name it, with message_store_backend still file. Copy the store into it: hmailserver --copy-store-to-object-storage. It runs beside the server, verifies every object, and goes on where it stopped when run again. Switch new mail to it: message_store_backend to object, taken within five seconds. Remove the local copies when you are ready: stop the server and run the copy again with --remove-source. Going back is the same the other way: message_store_backend to file, then hmailserver --copy-store-from-object-storage, then, once it says nothing failed, message_store_object_service to empty. When the store cannot be reached, mail is not lost. Door Answer for a message only the store holds A delivery Placed on the local disk and done; committed when the store answers again IMAP FETCH NO [UNAVAILABLE] POP3 RETR and TOP -ERR [SYS/TEMP] The webmail and REST 503 HM7473 says, once an hour at most, how many files wait on the local disk. HM7471 is the store refusing this server's credentials (401, 403), which is never taken for an absent message. HM7476 names a message that is neither on the disk nor in the store; no placeholder is written for it while a store is named. What is thinner than it could be: one connection per request; one committer, a pass at a time; credentials are the two the settings hold, with no instance role, no workload identity and no Azure AD token; the Import Tool and the data directory synchronizer walk the local disk only; a changed prefix is not a move. The requests were proved against MinIO and Azurite on the project's lab on 25 September 2026. The server's fixture needs that lab, is skipped on a bench without one, and has not run in the Linux suite. Mailbox encryption at rest Schema 6099. A mailbox whose messages are stored encrypted to an X25519 key of the account's own, with AES-256-GCM, as the container's last stage, after compression. The key opens when the person signs in with their password and closes MailboxKeyIdleMinutes after the owner's last use (15 by default, 1 to 1440). Mail that arrives while nobody is signed in is encrypted all the same, because encrypting needs only the key's public half. When a password is lost, the domain's recovery key opens the key again. It is off for every account until an administrator switches it on for that account. What it protects: a disk taken out of the machine, a snapshot of the volume, a backup archive, the data directory copied somewhere, the database dumped, once the account has signed in with its password after the switch. What it does not protect. It does not protect the mail from the running server, which reads it while the key is open, nor from whoever runs the server, who can capture a password at sign-in. A person who needs that wants a zero-access mailbox, and the two are never on one mailbox. Until the first sign-in the key is pending, held under this machine's own secret: the machine's DPAPI key on Windows, and the key file .hmailserver-secret-key inside the data directory on Linux. On Linux a copy of the data directory together with the database therefore opens every pending key. An account that only receives mail and never signs in with its password stays pending. Switching it on, on the Control Deck or over REST: The domain's recovery key first: the domain page's Mailbox recovery key card, or PUT /api/v1/domains/{domain}/mailbox-recovery with {"passphrase": ..., "hint": ...}. The passphrase is at least 12 characters, and this server never stores it. Keep it where this server is not. Then the account: the Encryption at rest card, or PUT /api/v1/accounts/{address}/mailbox-encryption with {"enabled": true}. The switch-on ends every session the account had. A mail client sees its connection drop; the person signs in again with the password. Then what is already there: hmailserver --encrypt-store --account <address>, with the server stopped. Refused, with a sentence: an account whose domain has no recovery key; a zero-access mailbox; an account that signs in against a Windows directory; and an account with a TOTP second factor enrolled. The last runs both ways: a second factor cannot be enrolled on an account that has a mailbox key. Sign-in. IMAP and POP3 sign a keyed mailbox in only with its own password. An app password, an OAuth2 token, a client certificate or a Kerberos ticket is answered NO [UNAVAILABLE] (POP3: -ERR [SYS/PERM]), and the IMAP master user is refused whether or not the owner's key is open. The webmail refuses a passkey, single sign-on or an app password (403, mailbox_key). SMTP, ManageSieve and DAV are not refused, since they do not read the mailbox. Passwords. The person's own change wraps the key by the new password. An administrator's reset does so while the key is open or pending, and is refused while it is closed, with a sentence naming the recovery: POST /api/v1/accounts/{address}/mailbox-encryption/recover with the domain's passphrase and the new password. A lost password and a lost recovery passphrase together are the mail lost. Nobody can open it. DELETE /api/v1/accounts/{address}/mailbox-encryption?confirm=forfeit removes the key, and with it every message still encrypted to it, for ever. It is refused while a legal hold covers the account. Switching it off stores new mail as it comes. What was stored encrypted stays encrypted until hmailserver --decrypt-store, run with the server stopped and the domain's passphrase in the environment variable HMAILSERVER_RECOVERY_PASSPHRASE, never on the command line. A keyed mailbox stays in its domain. A move to another domain is refused (409); a rename inside the domain goes ahead. What stays in the clear: the queue and the copy a delivery works on, which is overwritten with zeros once placed, though a copy-on-write filesystem, a snapshot or an SSD may not honour that; the calendar and the address book; the quarantine, the archive and journal copies, and a forward; each message's row in the database, with its size, flags, folder and dates; logs and the trace. An encrypted mailbox holds no part once. A blob shared between accounts cannot be under one account's key, so every part stays inside the message's own container. The cost is the de-duplication, for these mailboxes only. Backups A backup with messages copies the data directory as always, with the containers, the Blobs directory and encrypted files as they are. It copies file by file while the server runs, so an archive taken while mail is delivered and deleted can hold a container whose part was written after the Blobs directory was copied: take the backup when the server is quiet. With an object store it also reads every message and blob that is only in the store into the archive; a store that cannot be read fails the backup. A full restore rebuilds the blobs' counts from the stored files. hm_storeblobs is not in the archive. An encrypted mailbox's key is carried with its account, and the domain's recovery key with its domain. A pending key on Windows opens only on the machine that wrote it; elsewhere the mailbox is opened with the recovery. On Linux it opens wherever the key file goes, and the archive does not carry that file. A restore onto a server that never turned compression on reads the containers the archive brought. Known limits in 6.3.4 Limit Where it is stated Encryption at rest is on REST and the Control Deck, with no webmail card and no Control Panel page. The person's own view is GET /api/v1/me/mailbox-encryption CHANGELOG, EncryptionAtRest.md No COM for the switch, the recovery or MailboxKeyIdleMinutes EncryptionAtRest.md A search over an encrypted mailbox reads every message, and works only while its key is open. The full-text index holds none of its words EncryptionAtRest.md With compression on, Message.Filename over COM names the stored form; with an object store it names a path that holds no file once committed CHANGELOG, MessageStore.md, ObjectStorage.md An account's key is never rotated; the domain's recovery key can be replaced EncryptionAtRest.md The encryption fixture runs on Windows and is not in the Linux suite EncryptionAtRest.md No zstd: deflate is the one stage MessageStore.md Where to read more The wiki page: Message Storage The documents: MessageStore.md, ObjectStorage.md and EncryptionAtRest.md, each with its error codes and what proves each claim Zero-access mailboxes: the wiki's Encryption for People, and on this forum End-to-end encryption The documentation page: Backup and restore On this forum: The Control Deck, where the cards and the settings form are, and Backup and restore, for what the archive holds and what it leaves out
  • 0 Votes
    1 Posts
    139 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) adds no schema step, so a 6.3.6 node and a 6.3.7 node both require schema 6108 and the version check at start does not tell them apart: upgrade both nodes all the same. On MySQL and MariaDB a lost database connection is reconnected and the statement run again, so the statement after a database restart or a failover no longer fails; and the first units of the active-active work are in the tree, and it is still not built. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release, adds no schema step either, so a node of 6.3.6, 6.3.7 or 6.3.8 requires 6108 and the check at start does not tell them apart: upgrade both nodes all the same. See the 6.3.8 announcement. This topic covers what hMailServer 6.3.4 offers for a second machine. Two operator documents describe one topology from two angles: WarmStandby.md, on what has to reach the standby and the constraints that decide the design, and HighAvailabilityRunbook.md, the active/passive procedure with a virtual IP and a health check. The one thing to know before anything else: exactly one hMailServer service may run against the database at a time. There is no clustering code and no active/active mode. Failover is starting a service on the other machine, which takes minutes, and a second service started against the same database while the first still runs delivers mail twice, with no error anywhere. Why exactly one service may run The code makes two servers destructive to each other in three ways: Startup unlocks the whole queue. PersistentMessage::UnlockAll runs update hm_messages set messagelocked = 0 for every queued message when the service starts, because locks held by a crashed server would otherwise stall those messages for ever. A second server started against the same database releases every message the first is delivering, and both then deliver them. Delivery selection is not coordinated. The queue is driven by select ... where messagelocked = 0 and messagenexttrytime <= now, and the lock is taken after selection, so two servers race that window even after both have started cleanly. External fetch has the same shape. PersistentFetchAccount::UnlockAll runs at fetch-manager start, and a POP3 account fetched twice with delete-after-download is mail split between two stores. The rehearsal of 9 September 2026 measured the first. With the primary holding a queued message locked, the standby was started on a second machine: it bound the same ports, because the listeners are rows in hm_tcpipports and both servers read them, and the lock went to 0. Nothing warned, on either side. So the standby is warm: installed, configured, patched along with the primary, and its service set to Manual start and left stopped. WarmStandby.md says Manual rather than Disabled, because a disabled service cannot be started by the runbook without an extra step. The HA runbook accepts either in its section 4 and its check-list, though the wiki's copy of it lists Disabled among the ways the pair is got wrong, for the same reason. Use Manual. What lives where State Lives in Reaches the standby by Domains, accounts, aliases, lists, rules, routes, IP ranges The database Sharing the database Message metadata, folders, IMAP state The database Sharing the database The delivery queue hm_messages rows and spool files on disk The database and the data directory Greylisting triplets, auto-ban ranges, login-failure state The database Sharing the database Message files (.eml) The data directory Replication or shared storage, below The [Settings] section Since 6.3.4 the database (hm_inisettings, schema 6042); the INI section is a cache Sharing the database [Directories], [Database], [Security], [GUILanguages] Each machine's own hMailServer.ini Configured per machine, deliberately [SettingsOverride] Each machine's own INI Not shared, on purpose PasswordPepper Each machine's own INI; no database and no backup holds it Copied by hand TLS certificates and keys, DKIM keys Files on disk; the database stores their paths The same files at the same paths Route, external-fetch and per-domain-relay passwords The database, DPAPI-protected They do not travel An account's second factor and the [Settings] secrets The database, sealed under the stored-secrets key (schema 6103) Sharing the database, once the standby holds the key The settings store, since 6.3.4. A standby that shares the database inherits the primary's server settings at its next start, without anything copying INI files. Each setting the primary changed since the standby's file was written is named in the standby's error log as HM5804 and its line rewritten: that is the copy catching up, not a fault, and it appears once. A stale file can still do two things. A setting deleted on the primary whose line the standby's file still carries is adopted back into the shared store when the standby starts, so delete it from the standby's file as well. And a key the standby's file holds that the primary never had is adopted the same way. Do not copy the primary's INI over the standby's. On Windows [Database] Password is sealed with the machine's own DPAPI key, and the copy breaks the standby's database connection outright. Treat "no [SettingsOverride] section on either machine" as part of the check-list: a key in that section is applied over whatever the database holds. On 6.3.3 it was different. The wiki records that the [Settings] section was not carried by the shared database there, and had to be copied into the other machine's INI after every settings change. The wiki also notes that [LDAP], [SendingLimits] and [SendingLimitsOverrides] stay in each node's file in 6.3.4, to be kept the same by hand. Paths must resolve on both machines. If the primary's row says C:\certs\mail.pem, the standby needs that file at that path. Rotated DKIM keys are written into <DataDirectory>\DKIM; a standby promoted with an out-of-date copy of that folder signs with a key file it does not have. What does not travel Passwords the server presents to somebody else. Route smart-host credentials, external fetch accounts, the per-domain relay password and SSL private-key passphrases are stored protected by Windows DPAPI with CRYPTPROTECT_LOCAL_MACHINE, and a blob written by one machine cannot be decrypted by another. After a failover the server keeps running, with local delivery, IMAP, POP3 and unauthenticated relaying untouched, but each such credential fails until it is re-entered on the standby. Failback breaks them the other way. Keep the list of credentialed routes, fetch accounts and relay-configured domains, with the passwords in a password manager. It is between zero and five entries on a typical installation. The wiki has a PowerShell script over COM that lists them; none of its getters returns a password. On Linux the server seals these with a key file, <DataFolder>/.hmailserver-secret-key. In the rehearsal the route arrived without its password, the standby reported HM6412, and the delivery attempt went out unauthenticated. A replication that copies the data directory wholesale copies that file too, and the standby then reads every secret the primary could. Decide which you want; the rehearsal excluded it. The administrator password. It lives in hMailServer.ini, not in the database. Until it is set on the standby, the standby has no REST API and no COM authentication. On Linux the command is sudo hmailserver --set-admin-password. On Windows WarmStandby.md names the Control Panel, but a standby installed with 6.3.4 is a new installation, whose service runs as NT SERVICE\hMailServer by default, and under that account the Control Panel's password card is refused: use hMailServer.exe --set-admin-password from an elevated prompt, which is new in 6.3.4. The rehearsal found this when the standby came up mute. PasswordPepper. With a different value on the standby, or none, no account whose password is an Argon2id or scrypt hash signs in there. The stored-secrets key, since 6.3.4. It is kept in each server's INI as [Security] StoredSecretsKey, never in the database, so the standby needs it carried: export it on the primary, elevated, with hMailServer.exe --export-stored-secrets-key FILE (sudo hmailserver ... on Linux), and import it on the standby with --import-stored-secrets-key FILE. If the standby's installer made a key of its own, remove its [Security] StoredSecretsKey line first, or the import is refused. Never copy the line itself; it is sealed under the primary's own DPAPI. Without the key every second factor refuses every code on the standby, and SRS, BATV, HS256 tokens and /metrics stop working there; the standby says so as HM7495 at each start. Hardening a public mail server has what the key seals, and what to do on an upgraded server, which has none until you make one. The message store: the two documents differ WarmStandby.md says replicate, and do not share. It argues against putting the data directory on an SMB share or a Cluster Shared Volume: The accept path flushes the spool file to disk before answering 250. Over SMB that guarantee depends on the file server and on the network between the machines, which is the failure the topology exists for. Local delivery copies the spool file once per local recipient, and the delivery loop rewrites message files; each becomes network I/O. Handles and oplocks held by a crashed primary take time to expire, so the standby can find its files locked by a machine that no longer exists. Cluster Shared Volumes are built for large block-oriented I/O, not millions of small files. It recommends a one-way scheduled robocopy /MIR of the data directory every few minutes, or DFS-R or storage-level replication, stopped as part of failover, so that a stale mirror pass from a half-dead primary cannot delete newly accepted mail. Messages accepted after the last sync are rows whose files are missing on the standby. The server treats a missing file as an error on access, not a crash, so a five-minute interval bounds the loss to five minutes of message bodies; metadata, folders and flags lose nothing. The document calls the SMB and CSV reasoning operational judgement built on code facts, not itself a code fact. HighAvailabilityRunbook.md says share. Both nodes must see the same [Directories] DataFolder on shared storage (SAN, NAS, a clustered file system or a cloud file share), with identical read and write access for both service accounts, and the database and the store on the same failover boundary. Its validation check-list begins with that. Neither document withdraws the other. Choose knowing what each claims. The database Use a server backend, SQL Server, MySQL or MariaDB, or PostgreSQL, with its own availability. SQL Server Compact is excluded: it is a single local file. Both machines must run the same build: REQUIRED_DB_VERSION is checked at every start and refuses a mismatch in both directions, so a half-upgraded pair fails safe at startup where the two builds require different schemas; 6.3.6, 6.3.7 and 6.3.8 all require 6108, so that check does not catch a pair split between them. Health probes and the load balancer The probes are on the metrics listener, enabled on both nodes. Since 6.3.4 these are stored settings: set them in the Control Panel (Windows), over /api/v1/settings/ini, with hmctl, or on a Linux node whose REST listener is not yet on with hmailserver --set-setting NAME=VALUE. Then reinitialise (Apply now, or POST /api/v1/server/reinitialize) or restart. [Settings] MetricsServerPort=8080 MetricsServerBindAddress=0.0.0.0 MetricsServerAuthToken=<32+ random characters> ShutdownDrainSeconds=30 Point the health check at /readyz. It answers 200 only when the server is Running and the database is connected, and 503 while stopping, while draining, or when the database is lost. The passive node's service is stopped, so its connection is refused and it is never routed to. /metrics answers 503 on a non-loopback bind until MetricsServerAuthToken is set; the probes need no credential. ShutdownDrainSeconds defaults to 0, which waits for nothing. Set it. The drain is /api/v1/server/drain, on the REST listener: GET its state, POST to enter, DELETE to leave. Monitoring and health has the three probes and the listener's rules. Two matter here: MetricsServerBindAddress takes an IP literal and nothing else, since a host name keeps the listener, and so the probes, from starting; and the listener serves one connection at a time, so no sub-second health check. Building the standby Install the same build as the primary, with the same drive letter and directory layout. Point its [Database] section at the same database server, verify that it connects, then stop the service and set it to Manual. Set up the store: replication from primary to standby, or the shared directory. Record the credential list somewhere that survives the primary. Set the administrator password, copy PasswordPepper, and import the stored-secrets key. Rehearse the failover at least once before it is real. Planned failover The drain as a state of its own and UpgradeOnStart are new in 6.3.4. On 6.3.3, step 1 is a plain stop with ShutdownDrainSeconds set. Drain A: POST /api/v1/server/drain (manage_server), or the Drain button in the Deck's update view. A closes its SMTP, IMAP, POP3 and ManageSieve listeners, pauses its delivery queue, and reports /readyz 503 draining. A new connection is refused at the socket, which every MTA retries. REST, metrics and web services stay up. Watch GET /api/v1/server/drain until drained is true. Stop A. Verify A is down: /readyz on A is refused. Stop the replication job, in whichever direction it runs, where the store is replicated rather than shared. Do it before B is started. The runbook has no such step, because it assumes a shared store; WarmStandby.md's failover has it. In a planned failover A's machine stays up, so a scheduled mirror pass from it would delete the mail B has just accepted. Move the VIP to B. Start B and wait for /readyz 200. If B's binaries are newer than the shared schema, B refuses to start (HM5011) unless its INI says [Database] UpgradeOnStart=1. Take the database backup before this step. One node at a time. Check the key on B: no HM7495, HM7492 or HM7493 in its error log. /readyz is 200 either way, so this is the check that finds a missing key. Confirm: a test message through the VIP, and /healthz reporting database: up and state: running. Then re-enter the route, fetch and relay passwords on B, as after an unplanned failover. Reverse the steps to fail back, the check of the key included; with a replicated store, follow Failback below, which resynchronises the store first. To take A off the air without a failover, do step 1 and then DELETE /api/v1/server/drain, which reopens the same ports. Unplanned failover The health check on /readyz fails; stop routing to A. Fence A: power it off, or isolate it from the storage and database network. Unreachable is not the same as dead. Stop the replication job, where the store is replicated. Move the VIP, or the DNS record or port forward, to B. Start the service on B. Its startup unlock now works for you: messages the dead primary held mid-delivery are released and retried. A message the primary had delivered but not yet deleted from the queue may be delivered again, which is the known, bounded cost. Re-enter the route, fetch and relay passwords on B. When A is repaired, bring it back stopped, as the new passive node. The two documents order the middle differently: the runbook moves the VIP and then starts B, and WarmStandby.md starts the standby and then moves the endpoint. Both put the fence first. Keep MX TTLs modest, 300 to 3600 s, as a standing decision. Sending servers retry for days, so mail queued remotely during the gap is delayed, not lost. Failback is the same with the roles swapped, plus one step first where the store is replicated: resynchronise the data directory backwards, because the standby has been accepting mail and the primary's store is now the stale one. Then stop the standby's service, replicate from standby to primary once more for the final delta, start the primary, move the endpoint back, re-enter the route, fetch and relay passwords on the primary, and set up the replication from primary to standby again. Verify, after either direction GET /readyz answers 200: the service is running and the database has answered a real round trip within 20 s. A message sent in from outside, read back over IMAP or POP3. A message sent out through each credentialed route. No ERROR entries in the application log since the failover. TLS on 25, 143, 110, 587, 993 and 995 presents the right certificate. What it does not do, and what has not been rehearsed No active/active, no sharding, no clustering code. The runbook puts them out of scope. Not seconds. Failover is minutes, and the timings on the wiki pages are the shape of a rehearsed failover, not a promise. No rolling restart on one node. A single node refuses connections for the length of an install. The drain's state is per process, so a pair drains one node at a time. The rehearsal was a Linux pair: an Ubuntu 26.04 standby, one PostgreSQL 18 database, an SSH tunnel standing in for a LAN route. Not rehearsed: a Windows-to-Windows pair, DPAPI itself, a file-replication product rather than a copy at the moment of failure, DNS or IP takeover, and failback. Where to read more The wiki pages: Warm Standby, with the worked commands and the recovery-window table, and High Availability Runbook, with the probe-lifecycle table and load-balancer configurations The documents: WarmStandby.md and HighAvailabilityRunbook.md The documentation page: A warm standby, active/passive high availability and Prometheus metrics and health probes On this forum: Upgrading to 6.3.4, Moving to new hardware, and Settings: what is not stock and when a save takes effect, for the settings store and [SettingsOverride]
  • 0 Votes
    1 Posts
    138 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.6 (6 October 2026) publishes the first container image, registry.gitlab.com/hmailserver/hmailserver:6.3.6; its installer exits 10 or 11 when a step after its files did not complete (it exited 0), and it makes the data, log, temp, database and event folders private to the administrators and the service. See the 6.3.6 announcement. hMailServer 6.3.7 (17 October 2026), a security release, publishes registry.gitlab.com/hmailserver/hmailserver:6.3.7 (also tagged 6.3 and latest); its Windows installers carry the .NET Desktop Runtime 10.0.13, installed only where no .NET 10 Desktop Runtime is installed at all, so an upgraded server keeps the one it had; at every installation and upgrade /ProtectFolders hardens the program folder, Bin and ControlPanel, since an installation in a folder that took its permissions from the root of a drive let any account that could sign in to the computer replace the programs (CVE-2026-107571): before upgrading one, follow ServiceAccount.md's steps, and where other accounts could sign in while it was so installed, reinstall into a new folder only the administrators may change, given to setup with /DIR=, since over an installation setup installs into the folder it finds; and setup waits ShutdownDrainSeconds plus 90 seconds for the service's stop, where it waited plus 30. Chocolatey and the apt and dnf repositories still publish nothing: no channel is live as 6.3.7 is made, and 6.3.7 is not on the Snap Store. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release, publishes registry.gitlab.com/hmailserver/hmailserver:6.3.8, now the image tagged 6.3 and latest, which reaches a MariaDB database, where the images of 6.3.7 and earlier carried PostgreSQL's client alone, and whose entrypoint takes the database and administrator passwords out of the environment before it runs any program; and its Helm chart starts a server, where the charts of 6.3.6 and 6.3.7 never started a pod. Its Windows setup ends with exit code 7 where another account than SYSTEM, the Administrators and TrustedInstaller controls the program folder, Bin or ControlPanel, or a folder or a link on the way to them, naming the icacls that deals with it; with exit code 12, and the service set not to start until setup runs again, where something is not as setup left it after the files; and with exit code 1 for an installation of the original hMailServer's 5.7, the reason in its /LOG and in hMailServer-setup-refused.txt in %TEMP%. Chocolatey's install now passes the installer /LOG, and that log records setup's command line, so give the administrator password in HM_ADMIN_PASSWORD, set in the elevated prompt, rather than with /adminpassword in --install-arguments. Once 6.3.8 is published, jobs started by hand send it to the Ubuntu PPA (add-apt-repository ppa:hmailserver/hmailserver), Fedora Copr (dnf copr enable progressiverobot/hmailserver), Docker Hub, Chocolatey (hmailserver-progressiverobot, listed once its moderators approve it), PyPI and the PowerShell Gallery; the signed apt and dnf repositories this topic describes are still not published (Channels.md). See the 6.3.8 announcement. This topic covers installing hMailServer 6.3.4 without a wizard: the Windows installer's silent switches and what a silent install does with the administrator password; where the winget and Chocolatey packages stand; the signed apt and dnf repositories and why they are not live; and, since 6.3.4, every setting from the environment and the four setting profiles, the two ways a deployment shapes a server before it starts. The one thing to know first: an unattended install leaves the administrator password unset unless you pass it, and 6.3.4 is installed by hand from the release page, because neither package manager carries it and no live update installs it (the 6.3.4 announcement). The Windows installer's switches The installer is Inno Setup, so it takes the standard switches and a few of its own. Switch Effect /SILENT No wizard; progress window only /VERYSILENT No wizard and no progress window /SUPPRESSMSGBOXES Suppress message boxes; some failure paths still raise one without it /LOG="<path>" Write an install log; it is the only record of what happened /DIR="<path>" Installation directory /COMPONENTS="server,admintools,controlpanel" server is the service; admintools registers the COM type library; controlpanel installs the Control Panel, which does not need admintools /adminpassword=<password> Custom: sets the administrator password (at least 5 characters) and hands it to the database tools /upgradetoken=<hex> Custom: the single-use token of a live update; not for people to type /useinternaldbms=true\|false Custom: true (the default) uses the bundled SQL Server Compact database /serviceaccount=virtual\|localsystem\|keep Custom: virtual is NT SERVICE\hMailServer, the default for a new installation; keep is the default for an upgrade /profile=NAME Custom, since 6.3.4: the setting profile a new installation applies, or none. Written and read, and not run (below) hMailServer-x.y.z-x64.exe /VERYSILENT /SUPPRESSMSGBOXES /LOG="C:\Temp\hmail-install.log" ^ /COMPONENTS="server,controlpanel" /useinternaldbms=true Treat a non-zero exit code as a failed install and read the log. The installer checks both that the database tool launched and its exit code, so a failed or cancelled schema step fails the install rather than passing silently. The log has the tool's code: from DBSetupQuick.exe, 0 is a database created or already up to date, 1 a failed create or a DBUpdater.exe that would not start, 3 a wrong or missing administrator password, and any other DBUpdater.exe's own code. A hm_dbversion still on the old value afterwards is the same failure. A silent installation does not approve folders or key files the configuration names outside the program folder, and leaves such a service on LocalSystem rather than moving it to the virtual account. The administrator password, silently A fresh silent install leaves the administrator password unset unless you pass /adminpassword=<password> or set HM_ADMIN_PASSWORD: without either, the AdministratorPassword key is left out of hMailServer.ini entirely. Set it afterwards with hMailServer.exe --set-admin-password, new in 6.3.4, from an elevated prompt, then restart the service. It takes the password from HM_ADMIN_PASSWORD or, when that is not set, from one line of standard input when that is a pipe or a file, and never from an argument. Prefer the variable: a pipe from Windows PowerShell 5.1 turns every non-ASCII character into ?, storing a password nobody typed. On Linux the command is hmailserver --set-admin-password. ServiceAccount.md and Upgrading.md write the Windows step as hMailServer.exe /SetAdministratorPassword. The Control Panel and COM are refused under the virtual account. A new 6.3.4 installation runs the service as NT SERVICE\hMailServer by default, and under that account hMailServer.ini is read-only to the service: the Control Panel's Administrator password card and COM's Settings.SetAdministratorPassword are each refused with a sentence. They set the password only where the service may write the file, as it may under LocalSystem, which an upgrade keeps unless it is answered otherwise. A silent upgrade of an installation that has a password still needs it for the database upgrade, and cannot prompt. Without it the database upgrade fails and the installer reports a non-zero exit code rather than waiting. With the password empty, a silent upgrade works. Since 6.3.4, HM_ADMIN_PASSWORD is the safer of the two. Inno Setup drops every double quote from its command line, so /adminpassword=ab"cd sets abcd, and anybody listing processes while setup runs can read the switch. The variable takes any character. Setup reads it only when neither /adminpassword= nor /upgradetoken= is given. Set the variable in an elevated prompt. Setup started without administrator rights elevates itself through the UAC prompt, and the variable does not cross it. Until 6.3.4 the password went unquoted on the database tools' command lines, so one with a space, a double quote or backslashes before one failed the database step with exit code 3. It now goes in HM_ADMIN_PASSWORD. winget and Chocolatey: not in the public repositories yet The manifests exist and are validated for each release, from the release's own Authenticode-signed installer. They are not in the public repositories: neither winget install ProgressiveRobot.hMailServer nor choco install hmailserver installs this fork until a first submission has been reviewed and merged. Do not run choco install hmailserver expecting this fork. PackageManagers.md records none on 15 September 2026, but on 27 September 2026 the community feed held an unlisted hmailserver package: the original project's 5.6.8, not this fork's. What the packages are built to do, once accepted: Both run the signed installer the release page offers with /VERYSILENT /SUPPRESSMSGBOXES /NORESTART. /SUPPRESSMSGBOXES is not optional: without it a database-setup failure shows a message box, silent mode answers it with the default button, and a broken install reports success. /TYPE=full is deliberately not passed, so an upgrade keeps the components already installed and does not put the Control Panel back on a server where it was left out. MinimumOSVersion: 10.0.14393.0 is Windows 10 1607, the floor of the .NET 10 Desktop Runtime the Control Panel needs. The administrator password is left unset, and neither package invents one. Set one before the server is reachable from anywhere but the machine itself. Uninstalling runs the installer's own uninstaller, which does not delete the message store, the database or hMailServer.ini. A pre-release is never submitted. apt and dnf on Linux: written, tested in a lab, not live The project keeps signed apt and dnf repositories on the update feed's site, https://updates.progressiverobot.com. They are not live. The job that makes them, package-repos, was tested on the lab machine on 26 September 2026 with a throwaway key and dummy packages, and publishes nothing until the owner has made the signing key. This is what a Debian or Ubuntu user will type once it is live; on a release with no suite, apt update answers 404 for its InRelease. sudo install -d -m 0755 /etc/apt/keyrings sudo curl -fsSLo /etc/apt/keyrings/hmailserver.asc https://updates.progressiverobot.com/hmailserver-repository-key.asc gpg --show-keys /etc/apt/keyrings/hmailserver.asc # compare the fingerprint with the one below echo "deb [signed-by=/etc/apt/keyrings/hmailserver.asc] https://updates.progressiverobot.com/apt $(. /etc/os-release && echo "$ID$VERSION_ID") main" \ | sudo tee /etc/apt/sources.list.d/hmailserver.list sudo apt update sudo apt install hmailserver The fingerprint does not exist yet: PackageManagers.md records it as (not made yet). There is no dnf repository at all until a stable release carries .rpm files built for each distribution, and 6.3.4 carries the generic one alone. Once one does, on Fedora and Enterprise Linux it is sudo curl -fsSLo /etc/yum.repos.d/hmailserver.repo https://updates.progressiverobot.com/rpm/fc44/hmailserver.repo and then sudo dnf install hmailserver, with fc44 replaced by the release's directory. What 6.3.4 carries limits what the repositories could hold. Its Linux packages are one generic x86-64 .deb and one generic .rpm, neither built for one distribution; hMailServer on Linux: what you get and what you do not says where each installs. In the repositories as PackageManagers.md describes them, the .deb belongs in the ubuntu26.04 suite, and the .rpm, built on Ubuntu 26.04, goes in no repository. On 26 September 2026 dnf install of the .rpm built from master that day resolved its dependencies on Fedora 43 and 44 and AlmaLinux 10, and was refused on AlmaLinux 9, whose glibc and libstdc++ are older than it asks for. Packages built for each distribution come from the release after 6.3.4. The packages carry no RPM signature of their own: the signed repository metadata carries every package's SHA-256. Not tried: the job on GitLab's runners, HTTPS end to end, arm64 and aarch64. Until it is live, First install on Linux is the walk. Every setting from the environment Since 6.3.4. Every [Settings] key and every hm_settings row the server reads has a name in the environment. Variable Sets HM_SETTING_<key> The [Settings] key, one of 241: HM_SETTING_MaxSubmissionsPerIPPerMinute=30 HM_PROPERTY_<name> The hm_settings row: HM_PROPERTY_maxmessagesize=20480 HM_SETTING_<key>_FILE, HM_PROPERTY_<name>_FILE The same, read from the file the variable names, such as a Kubernetes or Compose Secret; UTF-8, at most 64 KB, trailing line breaks stripped The prefixes and _FILE are written in capitals; the name after the prefix is matched without regard to case, and a key is stored in its own spelling. What is refused, before anything is stored: a name that is not a setting of the store its prefix names (MaxMessageSize after HM_SETTING_ is refused naming HM_PROPERTY_maxmessagesize); a misspelt prefix such as HM_SETTINGS_; a read-only property such as WebPushVapidPublicKey; the plain and the _FILE form of one setting together; a _FILE naming a directory, a missing path or a file over 64 KB, refused naming the path, never the content; the _FILE form of a setting a domain or an account may hold its own value of; and a value the setting's own REST check would refuse, in that check's words. maxmessagesize=off is refused rather than stored as 0, which is "no limit". The verb. hmailserver --config /etc/hmailserver/hMailServer.ini --apply-environment hMailServer.exe --apply-environment on Windows is the same verb from the same code. It opens the configuration and the database as the service does, so run it as the user the service runs as. It checks every value first and exits 1 having changed nothing when any is refused, then writes each through the setter PUT /api/v1/settings/... uses and prints a line per variable: set, unchanged or refused. A secret's value is never printed. Exit 2 is a configuration file that names no database. The verb's writes are not rows in the audit trail: its own output, in a container the container's log, says what it changed. Who runs it. No container image of 6.3.4 is published: the image is one you build yourself, from the Dockerfile in the tree. Its entrypoint runs the verb after --create-database and --upgrade-database and before the server. On Windows nothing runs it for you: a Windows service has no environment of its own that anything sets, so the verb is there for a script or a deployment tool. Run beside a running server, its values reach that server at the next reinitialise (Apply now), or the next start for a key marked restart. The environment wins, every start. The server records the managed names in the hm_settings row EnvironmentManagedSettings and refuses an administrator's change to one of them. Where What happens REST A PUT that would change a managed key is 409, with a sentence naming the variable; GET /api/v1/settings/environment lists every managed setting with no value COM (Windows) Every setter refuses with the same sentence Control Deck Set by the environment beside the key, with its value and no control Control Panel (Windows) The same mark, with the field disabled hmctl hmctl config changed prints (set by the environment); hmctl config export leaves managed settings out Unset the variable and restart to manage the setting in the server again. A value a _FILE supplied stays withheld after the variable is gone: REST answers (set), the Control Panel leaves the field empty, and it is never on a command line or in a log. The environment sets the server level only, and the two [SendingLimits] keys have no environment name. The container's own variables are not settings: the database's HM_DB_*, TLS included; HM_ADMIN_PASSWORD and HM_ADMIN_PASSWORD_FILE; HM_INI; HM_CREATE_DATABASE and HM_UPGRADE_DATABASE; HM_PROFILE and HM_PROFILE_CONFIRM. The four REST ones are aliases: HM_REST_PORT stands for HM_SETTING_RestApiPort. The Helm chart takes settings, properties and settingsFrom. In a container or on Kubernetes has the table, the chart, and the stored-secrets key a first start makes, to export at once. What has not been run. The verb has been run as hMailServer.exe --apply-environment on Windows only. Its POSIX half compiles; the image's entrypoint as a whole, the POSIX verb against a database, the chart and the Compose file have not been executed. There is no live reload from a ConfigMap change: a restart applies it. Profiles: the shape a new server starts in Since 6.3.4. A profile is the thirty or so settings that make a server the kind it is, each with a reason written from what the setting's reader does. Four ship as reviewed documents; there is no route that makes one of your own. Profile For What you would notice office One server that receives an office's mail and holds its mailboxes SPF, the MX check, DKIM and DMARC scoring; greylisting; the quarantine; auto-ban and lockout; a password policy and the breach check (31 entries) gateway Behind a filtering gateway that has already judged the mail Greylisting off; XCLIENT and an ARC seal trusted only from the gateway and sealer you name (17 entries) relay An outbound relay that holds no mailbox anybody reads IMAP and POP3 closed at the next restart; hard bounces suppressed; the trace kept three months (24 entries) sender A high-volume sender judged on its bounce and complaint rates The relay's shape, more deliveries at once, the feedback loops, the trace kept six months (32 entries) Every surface shows a diff first, then saves what it replaces, then writes, all or nothing. An entry that closes a listener, trusts an address range, turns a filter off or weakens a check, or shortens a window of days asks for a confirmation. A profile never touches a port, a path, a bind address, the database connection, a password or any secret, an IP range, Incoming relays, a DNS block list, a listener's TLS or a domain's own settings. What only you know, such as the gateway's addresses, is a step listed under the diff. Revert puts back each value the profile replaced and leaves alone one an administrator changed since. It puts back settings, not data: where a profile shortened a window of days, what the sweep deleted under the shorter window stays deleted, and a listener a profile closed opens again only at the next restart. There is one snapshot: a second apply waits for the first to be reverted or discarded. Where a profile is chosen at install time: The Windows installer offers the four on a page of their own, after the administrator password and before the service-account page, for a new installation only, with "None" chosen until another is. No diff is shown there: each profile's sentence names the listener, filter or trust it changes, and choosing it is the confirmation, so relay and sender close IMAP and POP3 with no further question. After the database step it runs hMailServer.exe --apply-profile NAME --once --if-new --confirm before it starts the service. Silently, that is /profile=NAME. Every failure is said, and the installation still succeeds with the shipped settings. The container applies HM_PROFILE once, before the server starts; HM_PROFILE_CONFIRM=yes confirms an entry that asks, and HM_PROFILE=none records that no profile is wanted. The verb itself: hmailserver --apply-profile NAME [--confirm] [--once] [--if-new] hmailserver --revert-profile Exit Meaning 0 Done, or nothing to do 1 Refused or failed 2 No such profile, or no database 3 An entry needs --confirm 4 The service is running 5 --if-new, and the server is in service On Windows the verbs refuse while the service runs (exit 4). On Linux nothing says whether a server is running, so run them with the server stopped, as the container's entrypoint does. --if-new does nothing on a server of ten accounts or more. A setting the environment manages is skipped, with its variable named. Afterwards a profile is chosen in the Control Panel's Maintenance, Setting profiles (Windows), the Deck's Checklist, COM's Settings.ApplyProfile (Windows), REST or hmctl. Read the diff at GET /api/v1/settings/profiles/{name}, or with hmctl settings profile NAME, which prints a diff_token. Apply with {"confirm": true, "diff_token": ...} on POST /api/v1/settings/profiles/{name}/apply, or hmctl settings apply-profile NAME with --confirm TOKEN: a diff that has moved since it was read is refused (409) and nothing is written. The way back is POST /api/v1/settings/profiles/revert; POST /api/v1/settings/profiles/discard drops the snapshot. The installer page with its /profile=NAME switch, the container's HM_PROFILE and the Linux verbs have been written and read, and not run: the installer was not built and the Linux server not compiled for this change, and no container was started. What it does not do No package configures the server: no password, no domain, no firewall port. No export of the configuration as an environment. Four profiles, no parameters, no profile per domain. Where to read more The wiki pages: Profiles and Environment and Running in a Container winget, Chocolatey, apt and dnf: PackageManagers.md The naming rule and every refusal: EnvironmentConfiguration.md Every entry of the four profiles: Profiles.md The silent upgrade: Upgrading.md The documentation page: Run the installer On this forum: Upgrading to 6.3.4, Installing on Windows for the wizard's pages, and Behind a gateway for the gateway profile's steps.
  • 0 Votes
    1 Posts
    141 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) has the migrations as a card on the Control Panel's Backup, restore & migration page, where this topic says there is no Control Panel page; and a message collected from an external account is sent to SpamAssassin with the Return-Path the other server recorded, where it was the guess from the From field, so its SPF and envelope rules may score such mail differently. See the 6.3.7 announcement. The Windows installer of hMailServer 6.3.8 (2 November 2026) refuses an installation of the original hMailServer's 5.7 (5.7.0, its pre-release builds 2788 and 2843, 5.7.1 and 5.7.2), which this fork's upgrade chain has no step from, before it changes anything, and the installers of 6.3.7 and earlier must never be run over one: such a server's mail comes in by a migration of kind imap, as Migration.md's From the original hMailServer says. See the 6.3.8 announcement. This topic covers every route 6.3.4 offers for bringing existing mail into this server: the webmail's Other accounts page for one person, an external account with the mirror on for a few mailboxes that must stay in step, migration jobs for hundreds of mailboxes from an IMAP server, Google Workspace or Microsoft 365, the Import Tool for mbox files and a Maildir, and the PST import for Outlook data files. It ends with the mailbox move, for a mailbox that is already here and needs a new address or a new domain. The one thing to know first: every route needs the local account to exist. Create the accounts with the Import Tool's text-file import, POST /api/v1/domains/{domain}/accounts, a spreadsheet over REST and hmctl, or SCIM, and only then bring the mail. Moving an installation is a different subject: Upgrading to 6.3.4 for an upgrade and Moving to new hardware for a new machine. Which route You are moving Use Keeps Does not keep One person's own mailbox, by that person The webmail's Other accounts (since 6.3.4) Folders as copies, then new mail collected Rules, signatures A few mailboxes that must stay in step until the cut-over An external account with Mirror every folder on (6.3.3 and later) Folders, every message byte for byte, the five standard flags, the internal date Subscriptions, custom keywords, ACLs Many mailboxes from an IMAP server, Google Workspace or Microsoft 365 A migration, a job per mailbox (since 6.3.4, schema 6090) The above, with keywords, special-use folders, contacts and calendars Rules, signatures, shared mailboxes, ACLs, Gmail's labels as labels mbox files The Import Tool, one folder per file (Windows only) Every message, the date from the headers Flags A Maildir The Import Tool's Select Maildir (Windows only) The Maildir++ hierarchy, the flags in the file names Custom keywords Outlook PST files PST import, a migration of kind pst (since 6.3.4) Folders, messages rebuilt as Internet mail, contacts, calendar items, tasks Attachments by reference, OLE objects, distribution lists What PST import refuses Outlook itself, over IMAP Whatever Outlook uploads Anything Outlook does not put on the wire One person, from the webmail: Other accounts Since 6.3.4. Settings, Other accounts lets a person do the IMAP route for their own mailbox without an administrator. They pick the old provider (Gmail, Outlook.com, Yahoo Mail and iCloud Mail fill in the server, port and encryption), give the user name and password, choose whether to leave the mail there, and tick Bring the folders over too. It is an external account made through /api/v1/me/fetch-accounts, with these differences from one an administrator makes: The spam and virus filters are on for new mail; folders brought over arrive as copies, without the filters. The host must resolve to public addresses only, on port 110 or 995 (POP3) or 143 or 993 (IMAP), unless the administrator turns on reader_fetch_accounts_private_addresses. Schema 6104 marks such an account reader_created, and the host is judged again at every collection: a name that answers a private address later is not collected, and the error log says why (HM5909). The interval is at least 15 minutes, and a person has at most reader_fetch_accounts_max of them: 3 by default, and 0 takes the page away. A host on FetchOAuth2Hosts (outlook.office365.com by default) is refused from the page; only a server administrator adds one. Signing in rather than giving a password. Outlook.com no longer accepts a password from another mail server, and Gmail takes only an app password. Where an administrator has registered the server with Google and Microsoft, the page offers Sign in to Gmail and Sign in to Outlook.com: the person grants IMAP access there and the account logs in with XOAUTH2. No password is stored, and the permission can be taken back at the provider. The first collection then lists the folders to tick, with Gmail's All Mail, Starred and Important unticked so that nothing arrives twice. The registration is set on PUT /api/v1/settings: import_oauth_redirect_base_url (the redirect URI to register is that plus /portal/oauth/callback), import_oauth_google_client_id and import_oauth_google_client_secret, import_oauth_microsoft_client_id and import_oauth_microsoft_client_secret. The Google client needs the Gmail API and the https://mail.google.com/ scope; the Microsoft Entra registration needs the delegated IMAP.AccessAsUser.All and offline_access permissions. Until Google verifies the app, only the test users named on its consent screen can sign in. A few mailboxes that must stay in step: the mirror With Mirror every folder on, the server's own IMAP client does what imapsync does. The switch is on the account dialog's External tab in the Control Panel (Windows) and on the Deck's fetch-accounts view; over COM (Windows) it is FetchAccount.ServerType = 1 and FetchAccount.MirrorFolders = true. Create the local account. Add an external account on it: server type IMAP, the remote host, port and security (implicit TLS on 993, STARTTLS on 143), the remote credentials, and the mirror on. Decide what happens on the far side. Days to keep messages at 0 deletes each message from the remote folder once it is filed here; any other value leaves it there, so the two servers stay in step for as long as the account polls. A migration usually leaves the messages in place until the cut-over, then one last poll, and the account is disabled. Poll. Download now starts a collection at once. A large mailbox is collected over several polls, and a message is recorded as collected only after it has been filed. A mirrored message is stored byte for byte: no X-hMailServer-ExternalAccount header, no delivery, no rules, no anti-spam or anti-virus, because it is a copy of mail the other server had already accepted. The remote flags and internal date become the local ones, and Archive/2025 becomes Archive.2025. Use a new external account for the mirror. One that has been collecting the INBOX without the mirror collects it once more when the mirror is switched on. A \Noselect mailbox is skipped with a line in the application log, OnExternalAccountDownload does not fire for a mirrored message, and subscriptions, ACLs, quotas, Sieve and vacation settings on the far side are not read. Many mailboxes: migration jobs Since 6.3.4, schema 6090. A migration is a batch the server runs itself: a job per mailbox, throttled, resumed after a failure or a restart, with a report per mailbox and the contacts and calendars beside the mail. It is administered under /api/v1/migrations and in the Control Deck's Migrations view; there is no COM interface, and in 6.3.4 no Control Panel page (in 6.3.7's Control Panel the migrations are a card on Backup, restore & migration). A migration names its source: kind imap (a password per mailbox), google or microsoft365 (each mailbox signed in to by its owner), or pst; concurrency (1 to 32, 4 by default); and messages_per_minute (0 for no limit). A Google or Microsoft 365 migration reads imap.gmail.com or outlook.office365.com on port 993 over TLS, and any other host is refused. A job is queued, running, paused, done, failed or cancelled, through the phases mail, contacts, calendars and report. POST /api/v1/migrations {"name": "Old server", "host": "imap.old.example"} POST /api/v1/migrations/{id}/jobs the CSV below, or one job as JSON POST /api/v1/migrations/{id}/start GET /api/v1/migrations/{id}/jobs/{job}/report GET /api/v1/migrations/{id}/summary.csv sourceuser,secret,target [email protected],Anna's old password,[email protected] [email protected],,[email protected] One request takes up to 5,000 lines. Every line is checked before any job is made, and a bad line refuses the whole request and names itself, so nothing is half added. A job into an account another migration is copying into now is refused (409). Google Workspace and Microsoft 365. A job added with an empty secret waits for its owner: the answer carries signin_url, a one-use link for that person to open, sign in as that mailbox and grant access. A sign-in as anybody else attaches nothing, and POST .../jobs/{job}/signin makes a new link. The scopes asked for are Google's https://mail.google.com/, https://www.googleapis.com/auth/carddav and https://www.googleapis.com/auth/calendar, and Microsoft's https://outlook.office.com/IMAP.AccessAsUser.All and offline_access with Microsoft Graph Contacts.Read and Calendars.Read. A work or school tenant may need an administrator's consent. What a job does. It maps the INBOX to the INBOX, a special-use folder (\Sent, \Drafts, \Junk, \Trash, \Archive) to the account's own folder of that use whatever it is called, and everything else to the same path. It opens each folder with EXAMINE, so nothing on the source is marked read, and stores each message byte for byte with its flags, keywords and internal date. A message too large is skipped as too_large; an account over its quota pauses the job. No rule, filter or scan runs over a copied message. Contacts and calendars follow: from an IMAP server by CardDAV and CalDAV found by RFC 6764 discovery (or dav_url), from Google by its CardDAV and CalDAV, from Microsoft 365 by Microsoft Graph. A phase that cannot run is skipped with its reason and never fails a job whose mail is copied. A job is one copy, not a synchronisation: move the MX records first and migrate after, or keep a mirror for the mailboxes that must stay in step. It survives being interrupted. After every 50 messages and at the end of each folder the job writes its resume marks, and a job that has run before reads back what it wrote, so each message arrives once. A failure time may mend is retried after 1, 5 and 15 minutes and then hourly; after 24 attempts in a row without a message copied the job is failed. Wrong credentials fail it at once, and POST .../jobs/{job}/retry with {"secret": "..."} takes a mended password. Throttling is threefold: concurrency per migration; MigrationConnectionsPerHost (4 by default, 1 to 64) across every migration from one source host, because a provider counts sessions per client address; and at most 64 jobs on the server at once. Who may. Reading needs view_accounts and writing manage_accounts in the target's domain. The server administrator creates a migration, and a domain administrator then sees and adds to it. The audit trail records migrations and jobs, never a secret's value. Jobs are not carried by a backup. HM7240 (a resume mark could not be written) and HM7241 (a message could not be filed) are the two codes that reach the error log for a job from an IMAP server, Google or Microsoft 365. A PST job says the same two as HM7370 and HM7371, and its contacts and calendars phases add HM7410 and HM7411. The report says, folder by folder, how many messages the source held, how many were copied and why any were skipped: duplicate, another_folder, too_large, refused, gone, empty. mbox files, a Maildir and accounts from a text file: the Import Tool The Import Tool is a Windows program (Start menu > hMailServer > Import Tool, or Addons\ImportTool\ImportTool.exe) that works through COM, each message going to the server through Utilities.ImportMessageFromFileToIMAPFolder; there is no Import Tool on Linux. It imports a folder of mbox files into one account, one IMAP folder per file; Inbox in any case is the INBOX, and a subfolder's files are a second run. Each message is dated from its Received or Date header. mbox carries no flags the tool could read, so every imported message is unread. Select Maildir points at the directory holding cur, new and tmp, and lists the INBOX and every Maildir++ folder beside it. The flags after :2, in a file name are set on the copy: S seen, F flagged, R answered, D draft, T deleted. A colon is not allowed in a Windows file name, so the tool also reads ;2, and the same marker written with an exclamation mark in place of the colon; a copy whose names lost the info part imports unread. Accounts from a text file. The tool's other button creates or updates the accounts of one domain from a comma-separated text file, three fields a line: the account's name without the domain, its password and its size limit in MB (0 for none). Where the breach check is on, it runs for each password the tool sets through COM's Account.Password, one request per account: run a large import against your own mirror of the endpoint, or with the check off. Outlook data files: PST import Since 6.3.4. The server reads a PST itself, with a reader of this project's own written from Microsoft's published specifications and no third-party PST code. It is a migration of kind pst: one job per file, each into one account. Off until a directory is named. A job names a file, never a path, and the file is looked for only in the subdirectory named for the target account's domain inside the directory pst_import_directory names (PstImportDirectory in hm_settings, set with PUT /api/v1/settings or on the Deck's Settings page). It is empty as shipped, and while it is empty every PST job is refused with No PST import directory is set on this server. D:\PstImport\ <- pst_import_directory D:\PstImport\example.com\archive.pst <- a job for [email protected] names archive.pst Copy the file into the domain's subdirectory, where the service's account can read it. A share on another machine is refused, and so is a name that climbs out of the domain's directory. Make the migration, POST /api/v1/migrations with {"name": "Archives", "kind": "pst"}, then jobs as CSV lines of file,password,target over REST or in the Deck, or one job as JSON with hmctl api POST /api/v1/migrations/{id}/jobs. There is no Import Tool button for it and no COM route. Start it. It needs manage_accounts and manage_mailbox in the target's domain. What comes in. The folders a person sees in Outlook, with Sent Items, Deleted Items, Drafts and Junk E-mail found by what the store says they are, never by their names. Every message is rebuilt as Internet mail, deterministically, so a resumed job knows what it filed. Read, unsent, flagged and replied state become \Seen, \Draft, \Flagged and \Answered, forwarded the keyword $Forwarded, categories keywords. Contacts go into the address book, calendar items and tasks into the calendars, notes and journal entries into mail folders as messages. ANSI files (Outlook 97 to 2002) are read. What it refuses, with a sentence saying what to do: the 4K-page layout Outlook 2013 and later write for an OST, an OST caching an Exchange mailbox, a file under Windows Information Protection, and a missing or wrong password. A file Outlook still has open pauses the job. A message over 128 MB of text and data together is left out and named. Migration.md is candid about its evidence: no password-protected PST and no OST from Outlook were at hand. The reader runs in a process of its own (hMailServer.exe --pst-read on Windows, hmailserver --pst-read on Linux, not a command for an administrator), killed on a pause, a cancel or a stop, or when it has not answered in ten minutes. A file that crashes it costs one message, not the service. This is crash containment, not a sandbox: the reader runs as the service's own account, bounded in memory (2 GB committed on Windows, 4 GB of address space on Linux). The fixture API.PstImport runs on Windows; nothing runs it on Linux yet. What is not migrated Rules and filters, signatures, out-of-office replies and forwarding: make them again here. Shared mailboxes and permissions; a Microsoft 365 shared mailbox, which nobody signs in as, cannot be a job. Subscriptions: every folder is created subscribed. Gmail's labels as labels. A google migration copies each message once, and the star arrives as \Flagged. Contact groups; from Microsoft 365, the default contacts folder and calendar only. A mailbox already here: renaming it, or moving it to another domain Do not copy it into a second account: that loses everything hanging off the account id, which is its rules, folders, application passwords, second factor, contacts, calendars and role. Move or rename (since 6.3.4) keeps the account's row, and leaves the old address as an alias unless you say otherwise. Relocating an installation, and moving or renaming a mailbox has where it is done, what moves and what is refused. Where to read more The wiki page: Migration The document, with every field, limit and refusal: Migration.md Renaming and moving a mailbox: MailboxMove.md The documentation page: Importing accounts and mail, the Import Tool On this forum: The Control Deck, where the Migrations view is, and The webmail portal at /portal, where Other accounts is.
  • 0 Votes
    1 Posts
    144 Views
    ProgressiverobotP
    This topic takes a freshly installed hMailServer 6.3.4 to its first working mailbox, proves it locally, and then lists the DNS records without which its mail either does not arrive or is filed as spam. The one thing to know first: the domain's Postmaster address box is the catch-all, not the postmaster@ mailbox the email standards ask for. Create postmaster@ as an account, and leave that box empty unless you want every misaddressed message accepted and stored. The paths below are the Windows Control Panel's; on Linux, which has no Control Panel, the same is done in the Control Deck or over the REST API. If you are learning, make the sample domain first (since 6.3.4) One button, on the Control Panel's Welcome page and on the Control Deck's Checklist view, or one command (hmctl sample-domain create, hmailserver --sample-domain create on Linux, POST /api/v1/setup/sample-domain) makes a throwaway example.test: three mailboxes, a distribution list of the three, one rule and eight delivered messages. The same button, or ... remove, takes it away and says what it removed; a domain called example.test that it did not make is never touched. All three mailboxes sign in with one password, which the server mints when it makes the sample: twenty characters of Crockford base32, grouped in fives. It is shown once, in the answer to the call that made the sample, and is stored nowhere, so write it down; if you lose it, remove the sample and make it again. Older wiki text that speaks of "one documented password" describes an earlier version of the feature and is out of date. Remove the sample before the server takes real mail. Its mailboxes are ordinary accounts that can sign in and relay. Add your domain Go to Accounts & domains → Domains, type the name and press Add domain, then select it and press Properties. On Linux, use the Deck's Domains view, or: curl -u Administrator -H "Content-Type: application/json" \ -d '{"name":"example.com","active":true}' \ http://127.0.0.1:8045/api/v1/domains postmaster is optional in that body and is left out here, because it is the same catch-all field. The call answers 400 if the name is not a valid domain name and 409 if it already exists. Since 6.3.4 the Control Panel's domain editor has fourteen tabs; the seven that 6.3.4 added are each off, or empty, until set. The fields that matter on the first day: Tab Field Default What happens when it is wrong General Domain enabled ticked Unticked: every RCPT TO is answered 550 Domain has been disabled. General Domain name The part after the @. Setting it to the host name (mail.example.com) is the classic first-day mistake General Postmaster address empty The catch-all. Set: no address in the domain can be refused. Empty: unknown recipients get 550 Unknown user Limits Maximum message size (KB) 0 0 falls back to the server-wide maxmessagesize, seeded at 20480 KB Limits Enable plus addressing off, + Off: alice+invoices@ is an unknown recipient Relay Relay host empty Where mail from this domain leaves through. This is not a route DKIM Enable signing off Enabling before the DNS record is live makes every message fail DKIM at the receiver In RecipientParser::CheckDeliveryPossibility, an address that matches no account, alias, distribution list or route falls through to the postmaster address and the resolution restarts with it. That is why the box is a catch-all. Add a mailbox Select the domain, type the address and a password under the account list, and press Create account; then Edit for the full editor. Field Value to use Notes Password Press Generate strong password 16 characters from a cryptographic RNG, shown once and copied to the clipboard Administration level Normal user Server administrator can manage everything. Give it only to yourself Quota (MB) 0 for unlimited Account enabled ticked Unticked: 550 Account is not active. at RCPT TO On Linux, make the account on the Control Deck or with POST /api/v1/domains/{domain}/accounts. Create at least [email protected] and your own mailbox. A full mailbox is refused during the SMTP conversation, not bounced: with RejectFullMailboxAtRcpt on, as it is by default, RCPT TO is answered 452 4.2.2 Mailbox is full., a temporary refusal, so the sending server holds the message and no backscatter is sent. Since 6.3.4, accounts can be imported from a CSV file over REST, with hmctl and with the PowerShell module, with a dry run that changes nothing; and an identity provider can make and remove them over SCIM. Three things about an account are set only on the Control Deck and over REST, not in the Control Panel: its kind (a person, a room or equipment), encryption at rest, and a webhook route. How an address is resolved Every RCPT TO is answered by one function, in a fixed order that ends at the domain's postmaster address; the full order, with domain aliases, plus addressing and the webhook route, is in Aliases, distribution lists and public folders. What it means on the first day: Question Answer Why did mail to a mistyped address get delivered? The domain has a postmaster address, so it fell through to the catch-all Why does alice+anything@ not work? Plus addressing is off by default and is per domain Why does an alias to an external address still count as local? Only at the first hop, so that it is treated as inbound mail and not as relaying Test it locally, before touching DNS The test below works because a fresh database's My computer IP range lets the server itself, on 127.0.0.1, send as one of its own domains without a password, while anyone may send you mail and nobody may relay through you without logging in. A refused direction is 550 Delivery is not allowed to this address.; one that needs authentication is 530 SMTP authentication is required. The two seeded ranges and what each allows are in IP ranges, auto-ban, lockout and sending limits. Send-MailMessage -SmtpServer localhost -Port 25 ` -From "[email protected]" -To "[email protected]" ` -Subject "First test" -Body "If you can read this, hMailServer is working." Or by hand with telnet localhost 25. The replies worth recognising: Line What it tells you 250-SIZE 20971520 The server-wide maxmessagesize, 20480 KB, in bytes 250-STARTTLS Present only when the port's connection security is STARTTLS optional or required 250 AUTH … Absent when the port requires TLS, or the connecting range requires TLS when authenticating 250 2.0.0 Queued (0.016 seconds) The message is on disk and in the database 250 2.0.0 Queued for delivery Not the same reply: the message was quarantined Then check Monitoring & troubleshooting → Delivery queue, which should be empty, and look on disk: a delivered message is one file, C:\Program Files\hMailServer\Data\yourcompany.com\you\a3\{a3f01e6c-....eml}. Its first Received header reads with ESMTP, and with ESMTPSA for an authenticated submission over TLS. The DNS records your domain needs Do them in this order. Replace yourcompany.com and 203.0.113.10 with your own. Record Example Without it A / AAAA mail.yourcompany.com. IN A 203.0.113.10 Nothing resolves; the MX points at nothing MX yourcompany.com. IN MX 10 mail.yourcompany.com. Inbound mail bounces as "no mail server for this domain" PTR 10.113.0.203.in-addr.arpa. IN PTR mail.yourcompany.com. Google and Microsoft refuse or spam-folder outbound mail SPF yourcompany.com. IN TXT "v=spf1 mx a:mail.yourcompany.com ~all" Anybody can forge your domain; DMARC cannot pass on the SPF side DKIM dkim._domainkey.yourcompany.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." Forwarded mail fails everything, because forwarding breaks SPF DMARC _dmarc.yourcompany.com. IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]" No policy and no reports A / AAAA and MX. Add an AAAA only if the machine has a public IPv6 address that answers. An MX must point at a host name, never an IP address, and that name must not be a CNAME. The MX name is what MTA-STS matches, what TLSA records hang off, and what the certificate on port 25 should be issued for. PTR. Only your ISP or hosting provider can set it. It should name the same host as your HELO/EHLO, which hMailServer takes from Connections & protocols → Protocols → SMTP, Host name (HELO/EHLO greeting), falling back to the Windows computer name when blank. The Control Panel's DNS records page does not check it, and says so. SPF. Start with ~all; move to -all once the DMARC reports have shown you every system that sends as you. Publish exactly one SPF record per name: two beginning v=spf1 is a permanent error at every receiver. RFC 7208 caps the DNS-querying mechanisms at ten. In the other direction, checking SPF on incoming mail as a scored test is off by default (usespf is seeded 0), but DMARC checking is on by default and evaluates SPF and DKIM itself. DKIM. On the domain's DKIM tab click Generate key pair…: it creates an RSA-2048 key, saves the private key and shows the exact record, with a Copy DNS value button. dkim is the default selector. Switching signing on is gated on the published record matching. Ed25519 is supported too, with a key you supply. Since 6.3.4 a rotation policy per domain generates the new key under a dated selector (s20260915), checks the record every fifteen minutes and switches signing only once it has carried that key for a propagation period. It never edits DNS, and it is off until switched on. DMARC. Start with p=none, read the reports for a few weeks, then tighten to p=quarantine and p=reject. Do not start at p=reject. The DNS records page pre-fills rua= with the domain's postmaster address and omits the tag when there is none. Since 6.3.4, name that mailbox as the domain's report mailbox and the server parses the DMARC and TLS-RPT reports that arrive there and charts them on the Control Deck's DMARC page. Nothing read there acts on any mail. Optional hardening, at any point afterwards: Record Name Notes MTA-STS _mta-sts.yourcompany.com. IN TXT "v=STSv1; id=20260908120000" hMailServer serves the policy itself once WebServicesHttpsPort is set and the listener has a certificate for mta-sts.yourcompany.com; MtaStsPolicyMode defaults to enforce, so set it to testing to start TLS-RPT _smtp._tls.yourcompany.com. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]" Asks other servers to tell you when TLS to you failed TLSA _25._tcp.mail.yourcompany.com. IN TLSA 3 1 1 <sha256 of the certificate public key> Only meaningful if your zone is DNSSEC-signed. Re-publish before you rotate a certificate, never after CNAME autoconfig, autodiscover, mta-sts → mail.yourcompany.com. For client autoconfiguration and the policy host A / AAAA / CNAME openpgpkey.yourcompany.com Since 6.3.4, only if you publish OpenPGP keys BIMI is read, not published: since 6.3.4 the webmail shows a sender's logo, but this server publishes no BIMI record for your domains. Checking your work Give DNS an hour to propagate. Monitoring & troubleshooting → DNS records lists, per domain, the exact SPF, DKIM, DMARC, MTA-STS and TLS-RPT records to publish, with a Check button per record: Verdict What to do Not published Publish it Published but different Compare the two; the page shows what is there Published and correct Nothing Lookup failed A resolver or network problem, not a DNS-content one. Retry On Linux the Control Deck has a DNS records view. On either platform the first-hour checklist (since 6.3.4) judges the spf, dkim and dmarc items itself, for the first twenty active domains. From a command line: Resolve-DnsName yourcompany.com -Type MX Resolve-DnsName yourcompany.com -Type TXT Resolve-DnsName dkim._domainkey.yourcompany.com -Type TXT Resolve-DnsName _dmarc.yourcompany.com -Type TXT Resolve-DnsName 10.113.0.203.in-addr.arpa -Type PTR Being trusted by the servers you send to takes deliverability further. The ports your users will need The create scripts seed four listeners, SMTP 25 and 587, POP3 110 and IMAP 143, with no encryption. Since 6.3.4 a new installation sets all four to STARTTLS optional with the self-signed certificate and requires TLS for authentication from the Internet IP range; an upgraded server is left as it was (Hardening a public mail server). Ports 465, 993 and 995 are not created for you: add them on Connections & protocols → TCP/IP ports once you have a certificate. A TLS port with no certificate selected does not start (HM5113, Error initializing SSL. Certificate not set.); a port already in use is HM4316. Which connection security each port should have, and why STARTTLS optional belongs on port 25 alone, is in Connecting a mail client; which ports to open in the firewall is in Every port hMailServer listens on. Before you tell anyone the address postmaster@ exists as a real account. MX, SPF, DKIM and DMARC are published and verified. A certificate is installed and the client ports created: Let's Encrypt certificates are automatic. The administrator password is a PBKDF2 one. 6.3.4's installer writes it so; the wiki's checklist item about changing it from "the installer's MD5" describes 6.3.3 and earlier. A backup that you have restored once. What it does not do, and where to read more Nothing here publishes a DNS record for you: the Control Panel builds the text and checks it, and the DKIM rotation never edits DNS. The PTR record is your provider's to set. More on the records above: DKIM rotation, DMARC and TLS reports, and Requiring TLS for MTA-STS, TLS-RPT and DANE. The webmail and browser administration are separate topics: The webmail portal at /portal and The Control Deck. Sources: the wiki's Your First Domain and Mailbox, DNS for Your Domain and Ports Reference; SampleDomain.md and SetupChecklist.md; and chapters 6, 8 and 22 of the public documentation.
  • 0 Votes
    1 Posts
    149 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) adds no schema step (6108, as 6.3.6). Its x64 installer's MySQL and MariaDB client is MariaDB Connector/C 3.4.11, built from MariaDB's source, in place of the 3.4.9 that 6.2.3 to 6.3.6 carried (CVE-2026-107819), with seven authentication plugins where 6.3.6's had nine: a database account that signs in with MySQL Enterprise's Windows authentication can no longer sign in, and the server then cannot open its database; the ARM64 installer carries no MySQL or MariaDB client. On MySQL, MariaDB, SQL Server and SQL Server Compact an insert that forgives a duplicate key now forgives that alone and any other failure is reported (HM5032), and on MySQL and MariaDB a lost connection is reconnected and the statement run again, so an error log that was quiet may now name failures that were hidden. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026), a security release, adds no schema step either. Through MariaDB Connector/C, the client the Windows installer bundles and the Linux server loads, TlsMode=verify-full without TlsCaFile to a MySQL or MariaDB server on this machine (by a loopback address, localhost, a Unix socket or a named pipe, or through a Server= list that holds one) is refused with HM7535, since in 6.3.4 to 6.3.7, with Connector/C 3.4.1 or later, neither the certificate's issuer nor its name was checked there: before upgrading, name in TlsCaFile a CA that issues nothing but the database's certificates, the one that issued its certificate, or set TlsMode=require. A TlsCaFile whose name holds a character outside ASCII, which in 6.3.4 to 6.3.7 could reach the client as no CA file at all, is now handed to it as named; on Windows a TlsCaFile or PostgreSQLSslRootCert name the client cannot be handed as named is refused with HM7534, so give the file, and the folders above it, an ASCII name; and on Linux, where the distribution's client is older than Connector/C 3.4.11 and not patched, name the CA in TlsCaFile. See the 6.3.8 announcement. This topic covers the four databases hMailServer 6.3.4 can keep its data in, what each needs before the schema can be made, how the connection to the database server is encrypted since 6.3.4, and how to move an installation off the built-in engine. The one thing to know first: on MySQL and MariaDB, 6.3.4 needs MySQL 5.7.9 or MariaDB 10.2.2 or later, because since schema 6102 every table is utf8mb4. On an older server the create script stops at its first table, and an upgrade from 6.3.3 stops at its first step, 6040 to 6041, with error 1071 and nothing changed. What the database holds, and what it does not hMailServer stores accounts, domains, message metadata and settings in the database. The message bodies are files on disk, one per message, at <DataFolder>\<domain>\<local part>\<xx>\{guid}.eml, so the database stays small relative to Data\. Since 6.3.4 the database is also the settings store: the [Settings] keys of hMailServer.ini are rows from schema 6042, the file's section is the server's copy, and a line edited there for a key the database holds is named in the error log as HM5804 and does not take effect. Where the service may write the file the line is put back; where it may not (a new Windows installation under NT SERVICE\hMailServer, a Linux package installation) the copy is not refreshed, the edited line stays, and the database's value is used. The [Database] section itself stays in the file, because it says how the store is reached. The four choices Database [Database] Type What is bundled What you must have first Built-in (SQL Server Compact 4.0 SP1) MSSQLCE SSCERuntime_x64-ENU.msi, installed by setup Nothing. This is the default MySQL / MariaDB MYSQL MariaDB Connector/C 3.4.9 as Bin\libmysql.dll (3.4.11 since 6.3.7, with seven authentication plugins where 6.3.6's had nine) A reachable server, MySQL 5.7.9 or MariaDB 10.2.2 or later, an empty database, and a user that may create tables Microsoft SQL Server MSSQL Nothing; the OS's own data stack The same, including Express edition PostgreSQL PostgreSQL libpq from PostgreSQL 18.3 The same On Linux there is no built-in database: a Linux server keeps its data in PostgreSQL or MySQL/MariaDB, and hmailserver --create-database refuses SQL Server and SQL Server Compact by name. For MySQL or MariaDB install the client library yourself, because the server opens libmariadb.so.3 at run time: libmariadb3 on Debian and Ubuntu, mariadb-connector-c on Fedora and RHEL. Databases on Linux has the steps for each backend, and First install on Linux the order of the whole first install. Two things about the built-in engine. It runs on a single database connection: NumberOfConnections is forced to 1 for MSSQLCE. And Microsoft has withdrawn the SQL Server Compact download; the copy in installation/SQLCE/ is the archive of record, and it still installs. MigratingDatabaseBackend.md calls it "the one real dependency liability in this tree". For anything up to a few dozen mailboxes it is adequate, and the last section of this topic is the way off it. How the four behave differently MS SQL Server PostgreSQL MySQL / MariaDB SQL Server Compact DDL in a transaction Mostly Yes, the only one of the four No, commits as it goes No, commits as it goes A failed schema step leaves Normally nothing applied Nothing applied A partly built database A partly built database Isolation the server uses READ UNCOMMITTED its default its default its default Transactions used at all yes yes only if every table reports InnoDB at connect time asked for, but a no-op: SQLCEConnection::BeginTransaction returns true without starting one (Roadmap.md) Choose it when You already run SQL Server You want a failed schema step to roll back cleanly You already run it As a new installation's default, up to a few dozen mailboxes; as the target of a migration, only for a test rig MySQL and MariaDB since 6.3.4 MySQL 5.7.9 or MariaDB 10.2.2, or later. Since schema 6102 every table is utf8mb4 under utf8mb4_general_ci, InnoDB, with ROW_FORMAT=DYNAMIC, and a key over a varchar(255) is then 1020 bytes: more than the 767 bytes an InnoDB key part may be in the older COMPACT rows, and within the 3072 bytes these releases, as they ship, take. Why. MySQL's utf8 is three bytes a character, so an emoji in a contact, a folder or a signature was refused with error 1366 under strict mode and made a question mark without it. utf8mb4_general_ci is the twin of utf8_general_ci, so an address or a folder compares as it did, but it does not tell characters outside the Basic Multilingual Plane apart: two address books called Family 👪 and Family 🎉 are one name to its unique index. Creating the database yourself: CREATE DATABASE hmailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL PRIVILEGES ON hmailserver.* TO 'hmailserver'@'%'; The 6102 step also changes the database's own default character set, which needs the ALTER privilege on the database, not only on its tables. Every table must end up InnoDB: the server issues transactions only if all of them report it at connect time, and silently does not otherwise. A database the server creates itself is made utf8mb4 already. The 6.3.4 upgrade on these two rebuilds every table, and on a server older than 5.7.9 or 10.2.2 it stops at 6041 with error 1071, having changed nothing. Databases on Linux covers that step in full, its time, disk and duplicates (error 1062) included, and Upgrading to 6.3.4 the rest of the upgrade. Creating the database on Windows The wizard asks only "built-in or external". For the built-in engine, DBSetupQuick.exe creates the database at the current schema version, 6104. For an external engine, DBSetup.exe runs after the file copy, asks for the connection, and either creates the database or attaches one that exists; the secure defaults of a new 6.3.4 installation are applied only to one it created. Since 6.3.4 the administrator password reaches both tools in the environment variable HM_ADMIN_PASSWORD, never on a command line. The tools write [Database] in Bin\hMailServer.INI: Key Meaning Type MSSQL, MYSQL, PostgreSQL or MSSQLCE, matched case-insensitively Server, Database, Username, Password The connection. Server is empty for the internal engine PasswordEncryption Written by the server; says how Password is protected Port 0 uses the backend's default Internal 1 only for the embedded engine NumberOfConnections Pool size; default 5 The password in this file may be machine-bound. With ProtectStoredSecretsWithDPAPI=1, the default, the server writes Password through machine-scoped DPAPI (PasswordEncryption=6), and it cannot be decrypted on another machine. If you are also moving hosts, set the password on the new machine through DBSetup.exe rather than copying the encrypted one across. Under NT SERVICE\hMailServer, the account a new 6.3.4 installation runs as, hMailServer.INI is read-only to the service, and the COM methods the two tools call to write [Database] are refused before anything is made. The installer therefore runs its database step while the service is still LocalSystem. To change the database later, move the service to LocalSystem for the change and back (Installing on Windows has the commands), or edit [Database] by hand and restart. Encrypting the connection (since 6.3.4) The test lab measured on 25 September 2026 that the connection to MySQL and MariaDB through MariaDB Connector/C, the client bundled on Windows and the one Ubuntu packages, was never encrypted: every account was reached in plaintext, password exchange, queries and rows alike, and one made with REQUIRE SSL refused the server. Since 6.3.4 two keys under [Database] choose the behaviour: [Database] TlsMode=verify-full TlsCaFile=C:\hMailServer\certs\database-ca.pem TlsMode What the connection does (empty, the default) prefer: TLS whenever the server offers it, and the connection without it when the server offers none, or when the TLS it offers cannot be agreed on (one attempt with TLS and one without, for that failure only). The certificate is not checked prefer As the default, said explicitly disable No TLS. SQL Server's providers still encrypt the login itself require TLS or no connection; the certificate is not checked verify-full TLS or no connection, the certificate checked: its chain to a CA in TlsCaFile (or the system's trust store when that is empty; since 6.3.8 an empty one is refused, HM7535, on MySQL and MariaDB through MariaDB Connector/C to a database server on this machine), and its name or address against Server= TlsCaFile is a PEM file of CA certificates, read by verify-full and by nothing else, so that a CA file left beside require cannot quietly make it stronger or weaker than it says. A TlsCaFile that verify-full would read and that is not there refuses the connection (HM7531) rather than falling back to the system's store. Any other word in TlsMode is refused without an attempt (HM7530); verify-ca is refused by name, and PostgreSQL's own PostgreSQLSslMode=verify-ca remains for PostgreSQL. The keys are read at the service's start. Backend Before 6.3.4 Since 6.3.4, with nothing set MySQL / MariaDB, Windows and Linux Plaintext, always TLS whenever offered, unchecked; plaintext when not PostgreSQL, both platforms libpq's default, prefer Unchanged: libpq's prefer, or a PGSSLMODE in the service's environment, stands. TlsMode and TlsCaFile become sslmode and sslrootcert unless PostgreSQLSslMode and PostgreSQLSslRootCert are set, which win. verify-full with no TlsCaFile is sslrootcert=system on Linux (libpq 16 and later), and libpq's own default file on Windows, %APPDATA%\postgresql\root.crt of the service's account SQL Server, Windows The login encrypted and nothing after it, unless the server forced encryption The whole session encrypted through MSOLEDBSQL, when version 18 or later is installed, and through SQLNCLI for a failover partner; through SQLOLEDB only the login, unless TlsMode is require or verify-full SQL Server, Linux Refused: ADO is Windows (HM6390) Unchanged SQL Server Compact A file on this machine Unchanged On MySQL and MariaDB the old AllowUnencryptedConnection is still read and changes nothing: what it described, TLS preferred and plaintext allowed, is the default now. On SQL Server verify-full checks against the Windows certificate store; TlsCaFile is not read (HM7532), and ConnectionStringOptions naming encryption beside a TlsMode is refused (HM7533). Seeing it. The application log says how the connection is protected, once the service has started and again whenever it changes, for example Database connection: PostgreSQL: NOT encrypted: the server offered no TLS (TlsMode prefer, the default); the diagnostics run on the Diagnostics pages carries the same line under Server details. At the service's start a connection refused for TLS is the connect failure it always was, HM4354, with the database client's own reason and a sentence naming the setting to change. --check-database makes one connection with the TLS settings in the configuration, says what it got, and exits 0 connected, 1 not, 2 not configured. It changes nothing, so it runs beside a running service: hmailserver --config /etc/hmailserver/hMailServer.ini --check-database hMailServer.exe --check-database --config C:\hMailServer\try.ini What the server does not do for you. It cannot switch TLS on at the database server or give it a certificate. Under verify-full a MariaDB 11.4 server's own self-signed certificate is accepted without a CA file when the account's password proves it (MariaDB's zero-configuration TLS, for mysql_native_password, ed25519 and parsec accounts), and not once a CA file is named. A database that must refuse a plaintext connection should say so itself: REQUIRE SSL on the MySQL account, hostssl only in pg_hba.conf, Force Encryption on SQL Server. What is thinner than it could be. There is no editor for these keys in the Control Panel or the Deck. require on the MariaDB client is checked after the login, and an unencrypted connection is closed before a statement crosses it: by then a challenge and a response have crossed in the clear for mysql_native_password, caching_sha2_password and ed25519 accounts, and the password itself for an account that signs in through PAM or the clear-text plugin. verify-full refuses before the login. require protects against someone who can listen and not against someone who can alter the traffic, and prefer connects without TLS to a server that seems to offer none; only verify-full protects against someone on the path. And only a login with VIEW SERVER STATE lets SQL Server say whether its session is encrypted. Moving off the built-in database There is no migration tool. Upgrading.md records that as a gap; MigratingDatabaseBackend.md says it is not an omission, because backup and restore are already backend-agnostic: back up, repoint, restore. It works because the archive's XML carries no identity values, and the message store is addressed by domain name, local part and the file's own guid, never by id. Your mail files never move. Before you start. Write down how many domains, accounts, aliases and distribution lists there are, to verify against afterwards. Take a filesystem-level backup of the whole installation. Backup refuses to run if any message row points outside DataFolder ("All messages are not located in the data folder"). The restore refuses an archive taken by a newer hMailServer, so migrate on one version, then upgrade. Keep a copy of hMailServer.ini; the administrator password lives in it, so it carries over. And any mail delivered between the backup and the cutover is lost to the new database: hold inbound traffic, or accept the gap knowingly. Stop the service (Stop-Service hMailServer). Restart it for the backup itself, which runs through the running server, but stop accepting new mail first if you can. Take the backup on Maintenance → Backup & restore with all three of Back up domains, Back up messages and Back up settings. The archive lands as HMBackup <local time>.7z. All three are required: the restore refuses a combination that would delete something and put nothing back, and says so before it changes anything. Set BackupMessagesDBOnly to 1 before both the backup and the restore. Since 6.3.4 it is a stored setting that applies when it is saved, with no restart: set it in the Control Panel, over REST (PUT /api/v1/settings/ini/BackupMessagesDBOnly with 1) or with hmctl ini set BackupMessagesDBOnly 1. On 6.3.3 it was a [Settings] line in the file, read at start, so restart there. Without it, a normal restore empties the live data directory and copies the staged message store into it; with it that step is skipped and your .eml files are never touched. Remove it afterwards. Stop the service again (Stop-Service hMailServer) and wait for it to stop. Create the new database: empty, with a login that may create tables; on MySQL or MariaDB, utf8mb4. Edit [Database] to point at it, with Internal=0. If the service runs as NT SERVICE\hMailServer, move it to LocalSystem first (hMailServer.exe --service-account localsystem /Register) and back once the migration is verified: under the virtual account the COM methods DBSetupQuick.exe calls are refused before anything is made. Then: & "C:\Program Files\hMailServer\Bin\DBSetupQuick.exe" if ($LASTEXITCODE -ne 0) { throw "Database setup failed with exit code $LASTEXITCODE" } On MySQL and SQL Server Compact a creation that fails halfway leaves a partly built database: drop it and start this step again. Restore. Start the service (Start-Service hMailServer). At its first start against the new database the server adopts the file's [Settings] section into the empty store. Before you restore, check BackupMessagesDBOnly on the new database (hmctl ini get BackupMessagesDBOnly, or the Control Panel). The setting has to be identical for the backup and the restore, and the restore runs against the new database, which has a settings store of its own. If it is not 1 there, set it again, which applies when it is saved. That is the case when the old server could not write its line into the file, which it reported as HM5806 or HM5803, and under NT SERVICE\hMailServer, which does not write the file's copy. A restore without it is a normal one, which empties the data directory. Then: $app = New-Object -ComObject hMailServer.Application $app.Authenticate("Administrator", "<password>") $backup = $app.BackupManager.LoadBackup("D:\hmail-migration\HMBackup 2026-08-13 220511.7z") $backup.RestoreDomains = $true $backup.RestoreMessages = $true $backup.RestoreSettings = $true $backup.StartRestore() The server restarts itself at the end; that is expected. Verify all of these, because a partial restore looks like a working server: the domain, account, alias and list counts, against what you wrote down before you started; the folder list over IMAP as a real account; one whole message body; certificates, ports, IP ranges, routes and rules; one message in and one out; and an empty ERROR_hmailserver_<date>.log. Rolling back is exact until you delete the old database, which was only ever read: stop the service, put the original [Database] section back, start the service. Since 6.3.4 the settings come back with it, and a setting changed since the cutover is named as HM5804 on that start, which is the rollback working, not a fault. Ids change, so a script that recorded hm_accounts.accountid will break; key on the address instead. What it does not cover, and where to read more Moving the data folder to different storage is a separate operation, Relocating an installation to another drive; the path is [Directories] DataFolder. Moving to a new machine is Moving to new hardware, and why Windows to Linux is a rebuild. What the backup archive holds, and what it does not, is in Backup and restore. Sources: the wiki's Before You Install, Migrating the Database Backend and Installing on Linux; MigratingDatabaseBackend.md, DatabaseTls.md and Upgrading.md; and sections 3.5 and 33.3 of the public documentation.
  • 0 Votes
    1 Posts
    140 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026), a security release, has installers that carry the .NET Desktop Runtime 10.0.13 and install it only where no .NET 10 Desktop Runtime is installed at all; at every installation and upgrade /ProtectFolders hardens the program folder, Bin and ControlPanel, because in a folder that took its permissions from the root of a drive any account that could sign in to the computer could replace the programs the service runs (CVE-2026-107571; the default under Program Files was never affected); setup waits ShutdownDrainSeconds plus 90 seconds for the service's stop, where it waited plus 30; and the x64 installer's MySQL and MariaDB client is MariaDB Connector/C 3.4.11. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) is the release to install today, a security release: a new server runs hMailServer-6.3.8-x64.exe, Authenticode-signed and with its Sigstore bundle beside it, and a server on 6.3.4, 6.3.5, 6.3.6 or 6.3.7 installs it by the live update, with the exceptions the announcement lists. Its setup makes or hardens the program folder, Bin and ControlPanel before it copies a file or runs a program of the installation, and ends with exit code 7 where another account than SYSTEM, the Administrators and TrustedInstaller controls one of them, a folder or a link on the way to them, or a program, library or script in them (step 2 below; the four narrower cases of work item 78 that 6.3.7's notes left open); and it refuses an installation of the original hMailServer's 5.7 before it changes anything. See the 6.3.8 announcement. This topic walks through a new installation of hMailServer 6.3.4 on Windows: what the installer checks before it shows a page, each page of the wizard, the two pages 6.3.4 adds (the setting profile and the service account), what runs after the files are copied, and the first hour after it. The one thing to know first: 6.3.4 is installed by hand on every server. It carries an Authenticode signature and no Sigstore bundle, so no server's live update installs it; you download the installer, check it and run it yourself. If you already run hMailServer, read Upgrading to 6.3.4 instead. Before you download The installer enforces a floor rather than advising one: MinVersion=10.0.14393 and ArchitecturesAllowed=x64. On anything older, or on 32-bit Windows, setup stops with "hMailServer requires Windows 10 version 1607 (build 14393) / Windows Server 2016 or later, 64-bit." It needs an administrator, because it creates a service and writes to HKLM. The .NET 10 Desktop Runtime, which the database tools need, is installed automatically when missing; 6.3.7's installers carry 10.0.13, Microsoft's security release of 13 October 2026, and install it only where no .NET 10 Desktop Runtime is installed at all, so an upgraded server keeps the one it had. RAM is guidance only: 2 GB minimum, and nothing enforces it. Decide four things first, because the wizard asks for them: The administrator password. At least five characters. It is the master password for the whole server, not an email password. The database. The wizard asks only "built-in or external". If in doubt take the built-in one; Choosing a database covers the rest. The kind of server it is (since 6.3.4): the setting profile, below. The account the service runs as (since 6.3.4): NT SERVICE\hMailServer, unless something it would need, such as a data or backup folder outside the program folder or SQL Server with Windows authentication on the same machine, is planned for first. Plan antivirus exclusions for Data\, Database\ and Temp\, and somewhere to keep the exported stored-secrets key, apart from the database backups. Getting the installer and checking it Get hMailServer-6.3.4-x64.exe from the latest release on GitLab, or from https://updates.progressiverobot.com/download/hMailServer-x64.exe. Both now give 6.3.8, the newest stable release: hMailServer-6.3.8-x64.exe, SHA-256 4b9b9c3102cbed743f6c826289c54cec25c395d02208e55cdd3219187fbf8654, with its .cosign.bundle beside it; 6.3.4's own files are on its release page. 6.3.4 carries no .cosign.bundle. It was built and Authenticode-signed by hand, after GitHub suspended the project's account on 18 September 2026 and before the project's GitLab CI signing job existed. Its SHA-256 sums are in a file signed with the release tags' SSH key: the 6.3.4 announcement has the two commands that check a download against it. Then check the installer's own signature: Get-AuthenticodeSignature .\hMailServer-6.3.4-x64.exe | Format-List Status, SignerCertificate Status must be Valid, and the signer's subject names Progressive Robot Ltd. SmartScreen still warns: expect Windows protected your PC with the publisher's real name on it. winget install hMailServer and choco install hmailserver do not install this fork (Unattended installs, winget and Chocolatey). What the installer checks before it shows a page Choose Run as administrator. InitializeSetup() runs first: Check Result /adminpassword, or HM_ADMIN_PASSWORD (since 6.3.4), shorter than 5 characters Setup stops A window titled hMailServer Control Panel, hMailServer Administrator, hMailServer Database Setup, hMailServer Database Upgrader or DBSetup is open Setup stops: "…You must close down this application before starting the installation." Ports 25, 110 or 143 in use while the hMailServer service is not running Warning only: "…This indicates that there already is an email server running on this computer." Ignore the port warning and two servers will fight over port 25. The wizard, page by page Licence agreement. hMailServer is AGPL-3.0. Destination folder. C:\Program Files\hMailServer is fine. Since 6.3.7 setup's /ProtectFolders hardens the program folder, Bin and ControlPanel at every installation and upgrade: in a folder that took its permissions from the root of a drive, any account that could sign in to the computer could replace the programs the service runs (CVE-2026-107571, every version from 6.0.0 to 6.3.6); the default folder under Program Files was never affected. Since 6.3.8 setup makes or hardens them before it copies a file or runs a program of the installation: one of them, a folder or a link on the way to them, or a program, library, script or settings file in them, that another account than SYSTEM, the Administrators and TrustedInstaller controls, and a program folder on a share or on a volume that keeps no permissions, end the installation with exit code 7, each named with the icacls that deals with it and, where setup says so, a restart of Windows before it runs again (ServiceAccount.md). Components. server is the mail server, the database tools and the DB scripts. controlpanel is the administration application; without it you have no GUI. admintools, Remote administration support, registers the COM type library on a machine that is not the server, for your own scripts; the Control Panel does not need it. Database type. Use the built-in database engine unless you decided otherwise. If you choose external, DBSetup.exe runs after the file copy and asks for the engine, host, database name and credentials. Skipped when [Database] Type is already set: an upgrade never re-asks. Start menu folder. Administrator password. Minimum five characters; the two boxes must match. Since 6.3.4 a space, a double quote or a backslash reaches the database step as typed: the installer has the server store the password as PBKDF2 and hands it to the database tools in the environment variable HM_ADMIN_PASSWORD, never on a command line. Set one. While the value is empty, COMAuthentication::AttempAnonymousAuthentication grants server-administrator rights to any COM caller and RestApiServer::Start refuses to start the REST API. Server profile (since 6.3.4), on a new installation only. Service account (since 6.3.4). Ready to install. Pressing Install stops an existing hMailServer service and waits ShutdownDrainSeconds + 30 seconds for it (since 6.3.7, ShutdownDrainSeconds plus 90). If it will not stop, setup refuses to go on: "The hMailServer service did not stop within 30 seconds." Stop it in services.msc and run setup again; do not work around this. Install. The setting profile page (since 6.3.4) A setting profile is the thirty or so settings that make a server a particular kind, named. The wizard offers four, with None (the shipped settings) chosen until you pick another: Profile For office One server that receives an office's mail, holds its mailboxes and sends what its people write: SPF, DKIM, DMARC and greylisting on, spam held in the quarantine, auto-ban and account lockout, a password policy and the breach check gateway A server behind a filtering gateway that has already judged the mail: greylisting off, and XCLIENT and an ARC seal trusted only from the addresses and the sealer you name; putting the gateway's addresses in Incoming relays is yours to do (Behind a gateway) relay An outbound relay that holds no mailbox anybody reads: IMAP and POP3 closed, hard bounces suppressed, deliveries throttled per destination, every message traced sender The relay's shape for a high-volume sender judged on its bounce and complaint rates After the database step the installer runs hMailServer.exe --apply-profile NAME --once --if-new --confirm; --if-new leaves alone a database that already holds ten accounts or more. Every failure is said, naming the profile as not applied, and the installation succeeds with the shipped settings. In the wizard no difference is shown: each profile's sentence names the listener, the filter or the trust it changes, and choosing it is the confirmation, so relay and sender close IMAP and POP3 with no further question. Chosen later, from the Control Panel's Setting profiles page or the Control Deck's checklist, a profile shows its difference from this server before it changes anything. Either way it saves what it replaces and can be put back. A profile never sets a port, a directory, a [Database] value, a password or an IP range's Require SSL/TLS when authenticating. One caution from Profiles.md: the installer page is among what it lists as "written and read, and not run", because "the installer was not built for this change". The service account page (since 6.3.4) Until 6.3.4 the Windows service ran as LocalSystem, so a flaw reachable through SMTP, IMAP or POP3 was a flaw with full control of the computer. A new installation now runs it as NT SERVICE\hMailServer, the service's own virtual account: no password, no account to create, and it reaches only the folders it is granted. The machine The page offers The default No hMailServer service yet NT SERVICE\hMailServer, or LocalSystem NT SERVICE\hMailServer A service running as LocalSystem keep LocalSystem, or move it keep LocalSystem A service running as any other account keep that account, or move it back to LocalSystem keep the account An upgrade never changes the account unless this page is answered to. What the account is granted, by hMailServer.exe /GrantServiceAccess: Modify on the program folder's own Data, Logs, Temp and Database; Read on its other folders; Modify on Bin\hMailServerApiKeys.ini; nothing on hMailServer.INI; and a folder the configuration names elsewhere only once an administrator approves it. An attended installation asks (--confirm). A silent one approves nothing: when the configuration names a folder or key file outside those folders, it leaves the service on LocalSystem, working, and says why. The folders and the certificate and key files the configuration names are checked against those grants and reported: NOT APPROVED, a folder nobody has approved; OUTSIDE, a certificate or key file, which is never given Read where it is (move it into Data\Certificates, or approve its folder for Read); REFUSED, a folder that may never be granted. A folder is approved with --approve-modify "<folder>" or --approve-read "<folder>", or by answering Yes to --confirm; approvals are kept in HKEY_LOCAL_MACHINE\SOFTWARE\hMailServer\ServiceAccessPaths, a key only Administrators and SYSTEM may change. Approve only a folder you recognise: the service may then write in it. Never granted, approved or not: anything not a full path on a fixed local drive; a drive's root; the program folder, a folder above it, or anything in it but its Data, Logs, Temp and Database; the Windows folder, a Program Files folder, the user profiles folder or ProgramData\Microsoft, or any folder in or above one of them; ProgramData or a folder above it (a folder of your own inside it may be approved); and any path through a junction, a symbolic link or a mount point. --report-only says what would be done and changes nothing. Exit codes: 0 every grant made and everything named reached, or confirmed, 1 a grant or an approval failed, 2 not in an installation's Bin, 3 not elevated, 4 something outside not confirmed, 5 a folder that is never granted is named, 6 answered No. What only LocalSystem could do is refused with a sentence: the live update's apply (the update is still checked; on 6.3.4 and 6.3.5 an administrator downloads the installer from the release page, checks its signature and runs it, and from 6.3.6 installs the verified update with Install update on the Control Panel's Server status page on the server, with one Windows prompt), the auto-ban's Windows Firewall rules (AutoBanFirewall, HM7460; banned addresses are still refused by the server) and Utilities.MakeDependent. SQL Server with integrated authentication on the same machine needs a login first: CREATE LOGIN [NT SERVICE\hMailServer] FROM WINDOWS, then a user with db_owner. MySQL, MariaDB and PostgreSQL use a user and password, and nothing changes. hMailServer.INI is read-only to the service. A change of the administrator password over COM or the Control Panel's Administrator password card is refused; make it from an elevated prompt in Bin with hMailServer.exe --set-admin-password, which takes the password from HM_ADMIN_PASSWORD or, failing that, from standard input, and restart the service (ServiceAccount.md writes it /SetAdministratorPassword). Enrolling the administrator's TOTP second factor needs an account that may write the file, so move the service to LocalSystem for the enrolment and back. To move a service that runs as LocalSystem, run the installer again and choose the move, use the Control Panel's Windows service account card under Server limits & expert settings, or, from an elevated prompt in Bin: hMailServer.exe --confirm /GrantServiceAccess hMailServer.exe --service-account virtual /Register net stop hMailServer && net start hMailServer running the second line only when the first ended in 0. To go back, run the installer again and choose Move it back to LocalSystem, use the same card, or run hMailServer.exe --service-account localsystem /Register and restart the service. ServiceAccount.md records that the live registration "is exercised by hand on a throwaway machine; nothing here registers a service on the bench". What runs after the files are copied # Step What runs If it fails 1 .NET 10 Desktop Runtime Only when missing; since 6.3.4 pinned to 10.0.12 by version and SHA-512; 6.3.7 carries 10.0.13 and installs it only where no .NET 10 Desktop Runtime is installed at all Error dialog; exit codes 0, 3010 and 1638 count as success 2 Database backend SSCERuntime_x64-ENU.msi, built-in engine only Step 4 is skipped 3 Create the service hMailServer.exe --service-account <choice> /Register; a service to be moved is registered as LocalSystem here Step 5 is skipped 3a Administrator password (since 6.3.4) hMailServer.exe --set-admin-password The install fails 4 Create the database DBSetupQuick.exe for the built-in engine, DBSetup.exe for an external one chosen for the first time The install fails, naming the exit code 4a Secure a new installation (since 6.3.4) hMailServer.exe --apply-new-install-defaults Said and logged; the installation succeeds 4b Apply the profile (since 6.3.4) --apply-profile, above Said; the shipped settings stay 4c Stored-secrets key (since 6.3.4) hMailServer.exe --create-stored-secrets-key Said, with the command to run by hand 4d Move the service (since 6.3.4) /GrantServiceAccess, and only when that ends in 0, --service-account virtual /Register The service stays on LocalSystem, working 5 Start the service net START hMailServer, the verdict taken from the SCM "The hMailServer service was installed but did not start." The database step runs while the service is still LocalSystem, because it writes [Database] into hMailServer.INI through the service, which the virtual account may not do. ServiceMain reports SERVICE_RUNNING before it connects to the database, so a running service proves the process started, not that the schema is usable; step 4's exit code covers the schema. What a new installation starts with (since 6.3.4) Step 4a runs hMailServer.exe --apply-new-install-defaults after the database step, before the server first listens, and only on a database this installation created: on one DBSetup.exe attached the verb exits 5, which the installer only logs, and the installation succeeds. On a created database, ports 25, 587, 110 and 143 offer STARTTLS with a self-signed certificate that mail clients warn about, and the Internet IP range requires TLS for authentication, so a client on another machine must start TLS before it signs in; an upgraded server is left as it was. What each default is, how to replace the certificate (Let's Encrypt certificates are automatic) and how to apply the defaults by hand are in Hardening a public mail server. The stored-secrets key (since 6.3.4) On a new installation step 4c makes the key that seals each account's TOTP second factor and the [Settings] secrets in the database. It is kept in this machine's hMailServer.INI alone, never in the database or a backup, and an upgrade makes none. Export it and keep the file with the database backups, stored apart from them: # elevated, in the Bin folder .\hMailServer.exe --export-stored-secrets-key D:\keys\hmailserver-stored-secrets.key A database restored without it opens none of those secrets. Hardening a public mail server covers the key in full: what it seals, other nodes, and a key that is lost. Confirm it is running, and open the firewall Get-Service hMailServer should say Running. On a new installation that kept the page's default, services.msc shows it logging on as NT SERVICE\hMailServer. sc.exe qfailure hMailServer shows the recovery the installer set: restarts after 60, 120 and 300 seconds, the counter reset after 86400. The installer does not open firewall ports. Until you do, nothing reaches the server. One rule per port, in an elevated PowerShell; for SMTP: New-NetFirewallRule -DisplayName "hMailServer SMTP" -Direction Inbound -Protocol TCP -LocalPort 25 -Action Allow Which ports to open, and which to leave closed, are in Every port hMailServer listens on. The first hour: the checklist and the sample domain Since 6.3.4 the server judges its own first hour. Twelve items, each done, action_needed, cannot_tell or not_needed: a profile, a domain, an account, TLS on the submission port, passwords only over TLS, an SPF record, DKIM signing, a DMARC record, a backup destination, a second factor for the administrator, the stored-secrets key and the account the server runs as. It is the Control Deck's Checklist view, the head of the Control Panel's External setup page, GET /api/v1/setup/checklist and COM's Utilities.GetSetupChecklist. A button on the Control Panel's Welcome page and on the Deck's checklist makes a throwaway sample domain, example.test, with three mailboxes, a list, a rule and eight delivered messages; a second removes it. The server mints one password for the three mailboxes and shows it once, in the answer that makes the sample; Your first domain and mailbox has the detail. Remove the sample before the server takes real mail. Silent installation A silent install answers the two 6.3.4 pages with /profile= and /serviceaccount=, and leaves the administrator password unset unless /adminpassword= or HM_ADMIN_PASSWORD gives it. Always check the exit code: a failed database create fails the install. The switches, the profiles and the password are in Unattended installs. What the installer wrote {app}\Bin\hMailServer.INI is the only configuration file. Since 6.3.4 the database is the settings store: the file keeps [Directories], [Database], [Security] and [GUILanguages], its [Settings] section is the server's copy, and a hand edit to a key the database holds is named in the error log (HM5804) and does not take effect. A service that may write the file puts the line back at the next start; under NT SERVICE\hMailServer the copy is not refreshed and the edited line stays as you left it, so a line that survives a restart is still not the value in use. [Security] AdministratorPassword is PBKDF2, $h1$<iterations>$<salt-hex>$<key-hex>. {app}\Data, {app}\Events, {app}\Addons and the ini are left behind by an uninstall, on purpose. What it does not do, and where to read more The installer does not open the firewall, does not create ports 465, 993 and 995, publishes no DNS record, and never applies the secure defaults, a profile or a stored-secrets key to an upgraded database. All of this is Windows only: the Linux packages have no wizard, no Control Panel and no COM (First install on Linux). Sources: the wiki's Installing hMailServer and Before You Install; SecureDefaults.md, ServiceAccount.md, Profiles.md, SetupChecklist.md, SampleDomain.md and Upgrading.md; and chapters 3 and 4 of the public documentation. The release announcement is hMailServer 6.3.4: a password could run code on Windows servers with JScript scripting.
  • 0 Votes
    1 Posts
    169 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) lets the IMAP and POP3 listeners take the client's address from a PROXY protocol header, as the SMTP listener does, each from the peers on a list of its own; both are off by default, and in the Control Panel the PROXY protocol and XCLIENT are a card on Incoming relays, while API & monitoring is Monitoring & alerts. The ACME http-01 challenge responder serves up to 64 connections at once and closes one past that, where it served one at a time. The REST listener refuses a state-changing request that carries a credential when the browser reports it as cross-site (Sec-Fetch-Site, or an Origin that is not this server's); scripts, hmctl and the PowerShell module send no such header and are unaffected. 6.3.7's container image is registry.gitlab.com/hmailserver/hmailserver:6.3.7. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) has the web services listener close a connection, with no answer and before the TLS handshake, from an address whose IP range allows none of SMTP, POP3 and IMAP - an auto-ban's, or one of your own that allows nothing - while loopback and the addresses in WebServicesTrustedProxies are never refused by a range; so a range of your own that allows some addresses nothing now closes this listener to them as well, and with it ACME's http-01 validation when this listener answers it, MTA-STS, CalDAV, CardDAV, Autodiscover, autoconfiguration and the Web Key Directory: check that the certificate authority, the sending MTAs and these services' clients do not come from one. 6.3.8's container image is registry.gitlab.com/hmailserver/hmailserver:6.3.8, also tagged 6.3 and latest. See the 6.3.8 announcement. This topic lists every socket hMailServer 6.3.4 can open, inbound and outbound, and the firewall rules that follow. The one thing to know first: a new server listens on four mail ports and on nothing else. Every other listener is off until a setting gives it a port, three of them bind only to 127.0.0.1 when you do, and almost every outbound connection beyond DNS, SMTP delivery and the recipient's MTA-STS policy exists because an administrator turned a feature on. Write the firewall for what you turned on. Connection security per mail port, and what a new 6.3.4 installation sets on each, is in Connecting a mail client: IMAP, POP3, SMTP submission and the ports. Two kinds of listener, set in two places Mail-protocol listeners (SMTP, POP3, IMAP) are rows of the table hm_tcpipports, edited on Connections & protocols → TCP/IP ports in the Control Panel (Windows), on the Control Deck, over COM (Settings.TCPIPPorts, Windows) and over REST at /api/v1/ports, where a new row's address defaults to 0.0.0.0. A port you add, change or delete takes effect at a reinitialise (POST /api/v1/server/reinitialize, or the Deck's button that restarts the services in place) or a service restart. Service listeners are one key each, all off by default. Set their [Settings] keys in the Control Panel (Windows), over REST (PUT /api/v1/settings/ini/{name}), with hmctl ini set or hmailserver --set-setting NAME=VALUE; the Deck has no page for them. Since 6.3.4 the database holds these keys, so a line edited in hMailServer.ini turns no listener on (HM5804); Settings: what is not stock, when a save takes effect explains the store. Each listener's port, bind address and certificate files are among the 28 keys that wait for a reinitialise: Apply now in the Control Panel or the Deck, POST /api/v1/server/reinitialize, Application.Reinitialize over COM, or a restart. AcmeHttpPort applies when it is saved. The SNMP agent's settings are hm_settings rows instead, set on the Deck's Settings page under Server, over REST (/api/v1/settings) or with hmctl settings set, with no Control Panel page, and read when the agent starts, at a service start or a Reinitialize. The mail listeners Port Protocol Created by Default bind What it says on connect 25 SMTP Seeded in every new database The row's IP address (0.0.0.0 or :: is every address) 220 MAIL01 ESMTP: the Welcome banner from Connections & protocols → Protocols → SMTP, or the computer name, with ESMTP appended 587 SMTP submission Seeded The row's IP address The same banner 110 POP3 Seeded The row's IP address +OK POP3, or your welcome message 143 IMAP Seeded The row's IP address * OK IMAPrev1, or your welcome message 465, 993, 995 SMTP, IMAP, POP3 over implicit TLS You, once you have a certificate What you choose Nothing until the TLS handshake completes A listener bound to one internal address is the usual reason mail works from the LAN and not from outside. Two failures leave one port dead and the rest running: a TLS port with no certificate selected does not start (HM5113), and a port something else holds does not bind (HM4316). On Windows with WSL2 in mirrored networking mode, a socket bound inside WSL takes the port from Windows too, shows in neither Get-NetTCPConnection nor netstat -ano, and on the project's test bench only wsl --shutdown gave it back. The service listeners Listener Port key, default Bind key, default TLS How to tell it is up REST API, the Control Deck at /, the webmail at /portal, SCIM at /scim/v2 (since 6.3.4) RestApiPort, 0; 8045 is the conventional port RestApiBindAddress, 127.0.0.1 Required unless bound to 127.0.0.1, localhost or ::1 RestApi: Listening on <endpoint> (https) or (http, loopback only) in the application log Public web services WebServicesHttpPort, WebServicesHttpsPort, both 0; 80 and 443 to enable WebServicesBindAddress, 0.0.0.0 WebServicesCertificateFile and WebServicesPrivateKeyFile on the HTTPS port, or the ACME certificate on disk A URL it serves answers Prometheus metrics and health probes MetricsServerPort, 0 MetricsServerBindAddress, 127.0.0.1, an IP literal only MetricsServerCertificateFile + MetricsServerPrivateKeyFile; plain HTTP otherwise /livez answers 200 alive ManageSieve (RFC 5804) ManageSieveServerPort, 0; 4190 is the standard port ManageSieveServerBindAddress, 127.0.0.1 STARTTLS, only when an IMAP, POP3 or SMTP port has a certificate Its capability response ACME http-01 challenge AcmeHttpPort, 80 Every address Never Open only during an issuance SNMP agent, UDP, since 6.3.4 SnmpPort, 161, with SnmpEnabled off SnmpBindAddress, empty = every address SNMPv3 at authPriv instead of TLS A stranger gets no answer at all One port carries the REST API, the Deck and the webmail; there is no second port to open. It will not start without an administrator password, nor beyond loopback without a certificate, and with RestApiCertificateFile empty it takes the ACME certificate when one exists. For users to reach /portal, or an identity provider SCIM, it needs a certificate they trust, a reachable bind address and a firewall rule, or a TLS-terminating proxy in front (Behind a gateway, load balancer or reverse proxy). Turning it on is in The REST API: turning it on, credentials, roles, one-time codes. The web services listener binds every address by default. It serves the MTA-STS policy at /.well-known/mta-sts.txt (HTTPS only), ACME challenges, Thunderbird autoconfig, Outlook autodiscover and the Apple profile (HTTPS only), security.txt, CardDAV and CalDAV under /dav/ (HTTPS only; CalDAV since 6.3.4), and, since 6.3.4, the OpenPGP Web Key Directory (HTTPS only) and an S/MIME authority's root and CRL (plain HTTP). MtaStsHostingEnabled and AutoconfigEnabled default to 1 while both ports stay 0, which the server reports once at startup; the ports stay 0 on purpose, because port 80 may belong to IIS. The HTTPS port waits for a certificate, its own or ACME's, and says so in the log. Metrics. A host name or localhost as the bind address is refused (MetricsServer: Invalid bind address) and the probes go with the listener. Beyond loopback with no credential, /metrics answers 503 while the probes still answer. More, including the SNMP agent, is in Monitoring and health: Prometheus metrics, health probes, Grafana, OTLP and SNMPv3. ManageSieve has no certificate of its own and offers no STARTTLS, saying so in the log, until a mail port has one. The ACME challenge listener is not permanent. When WebServicesHttpPort equals AcmeHttpPort, the web services listener answers /.well-known/acme-challenge/; otherwise a temporary listener binds AcmeHttpPort for the issuance, and a failed bind logs Failed to start the http-01 challenge listener. Is the port in use? The client uses the http-01 challenge only, so port 80 must be reachable from the internet (Let's Encrypt certificates are automatic). The SNMP agent (since 6.3.4) answers nobody until SnmpAllowedAddresses names a manager; a datagram from any other address is dropped before it is read. Bind it to 127.0.0.1 or a management address, and name managers one by one: UDP does not prove a source address. Error 7420 is a port it could not bind, such as one the operating system's own SNMP service holds. Snmp.md lists the Linux bind to 161 under the packaged unit as not proved by any test. A Control Panel on another machine (Windows) reaches the COM API over DCOM, which must be permitted, after probing TCP port 135 (the RPC endpoint mapper). hmctl and the PowerShell module use the REST listener instead. Where Windows and Linux differ Where you set a listener. On Windows, the TCP/IP ports page, Connections & protocols → Web services & autoconfiguration, TLS & certificates → Certificates (ACME) and Monitoring & troubleshooting → API & monitoring (Monitoring & alerts since 6.3.7). Linux has no Control Panel and no COM: the Deck for the mail listeners and the SNMP agent, and REST, hmctl or hmailserver --set-setting for every listener's [Settings] key. The packaged RestApiPort=0 is stored at the first start, and a line edited afterwards turns nothing on; First install on Linux has the order. Low ports. The packaged hmailserver.service runs as its own user with CAP_NET_BIND_SERVICE, which it has for port 25 and which also lets the agent bind UDP 161. The database port. On Windows [Database] Port of 0 means the provider's own default. On Linux write it out (5432, 3306 it goes into the connection string as it stands. DNS. On Windows the custom servers under Connections & protocols → DNS resolver are given port 0, because the Windows DNS client supplies 53 itself and rejects an entry that names one; DNSServer is one IPv4 address with no port syntax. Containers. The image exposes 25, 110, 143, 465, 587, 993, 995, 4190 and 8045, and SNMP is published as UDP. No image of 6.3.4 is published; 6.3.8's is registry.gitlab.com/hmailserver/hmailserver:6.3.8, also tagged 6.3 and latest, and In a container or on Kubernetes has the rest. Proving what is listening On Windows, on the server and then from outside: Get-NetTCPConnection -State Listen -LocalPort 587 | Select-Object LocalAddress, LocalPort, OwningProcess Test-NetConnection 127.0.0.1 -Port 25 Get-NetFirewallRule -Enabled True | Where-Object DisplayName -like "*hMail*" Test-NetConnection mail.example.com -Port 25 openssl s_client -connect mail.example.com:465 -quiet On Linux, ss -ltnp lists listening sockets with their process, and the REST listener answers curl -u Administrator http://127.0.0.1:8045/api/v1/status. On Windows, Monitoring & troubleshooting → Server status lists the listeners the service brought up; senders timing out with nothing in the SMTP log means nothing reached a listener. What the server connects to On every server, with nothing turned on: Destination Port Setting If it is blocked DNS resolvers 53, UDP and TCP The machine's resolvers, or DNSServer Nothing is delivered externally, and DNSBL, SPF, DKIM, DMARC, MTA-STS and DANE stop at once Every recipient domain's MX host 25 None Messages stay queued and are retried, by default 4 times at 60-minute intervals, then bounced The recipient domain's MTA-STS policy, from mta-sts.<domain> 443 MtaStsEnabled, 1 Delivery to a domain with an enforcing policy falls back to what DANE and your TLS settings allow DANE and TLS-RPT need no port of their own: TLSA records come over DNS, and TLS-RPT reports are sent as ordinary mail. Delivery leaves from SMTPDeliveryBindToIP, or a rule's bind to address action; since 6.3.4 SMTPDeliveryBindToIP may name one address of each family, separated by a comma. Only when you configure it: Destination Port Setting, as shipped Smart host, or a route's host SMTPRelayerPort (25; 0 is read as 25), or the route's own Empty Database server The provider's port [Database]; since 6.3.4 MySQL and MariaDB use TLS on it when offered SpamAssassin spamd, ClamAV clamd 127.0.0.1:783, localhost:3310 Both off; no TLS An HTTP filter engine The URL's FilterHookUrl, empty The ACME directory 443 AcmeEnabled, 0; AcmeDirectoryUrl is Let's Encrypt The update feed, https://updates.progressiverobot.com, and the installer from the project's package registry on gitlab.com 443 UpdateCheckEnabled, 0 The password breach check, https://api.pwnedpasswords.com/range/ 443 BreachCheckEnabled, 0 (since 6.3.4) An object store: https://s3.<region>.amazonaws.com, https://<account>.blob.core.windows.net or MessageStoreObjectEndpoint 443, or the endpoint's MessageStoreObjectService, empty (since 6.3.4) OAuth2: a key set, an introspection endpoint, the outbound relay's token endpoint The URL's OAuth2JwksUrl, OAuth2IntrospectionUrl, OutboundOAuth2TokenUrl, empty An OpenID Connect issuer: <issuer>/.well-known/openid-configuration, then its token endpoint The issuer's oidc_issuer, empty (since 6.3.4) Outbound webhooks, alert webhooks, a sandbox service, a route of mail in to a webhook The URL's No subscription, rule or route; SandboxEnabled, 0 (all since 6.3.4) A syslog collector SyslogPort, 514 (UDP, TCP); 6514 is the registered TLS port SyslogEnabled, off (since 6.3.4) An OpenTelemetry collector The endpoint's OtelEndpoint, OtelMetricsEndpoint, OtelLogsEndpoint, empty The browser vendor's push service The service's WebPushEnabled, 0 (since 6.3.4) External accounts and migrations: another server's POP3 or IMAP; imap.gmail.com or outlook.office365.com on 993; graph.microsoft.com; CardDAV and CalDAV over https The account's; a person's own account 110, 995, 143 or 993 at public addresses unless reader_fetch_accounts_private_addresses is on Nothing until an account or a migration is made At a person's request in the webmail: since 6.3.4, a correspondent's OpenPGP key from their domain's Web Key Directory (lookup_wkd, on; never an address inside your network) or from https://keys.openpgp.org (lookup_keyserver, off), and a sender's BIMI logo (public addresses only, and only with AuthenticationResultsEnabled, which is 0); and, since 6.3.2, a one-click unsubscribe, posted to a public address only. Where an outbound request may go. Every outbound web request is https, or plain http to this machine only; https is verified against the machine's root store, host name checked, TLS 1.2 at least, so a private CA must be in that store (or in MessageStoreObjectCaFile for an object store). Outbound webhooks may reach a private address, on purpose; a route of mail in to a webhook never reaches a link-local or cloud metadata address, and one not made by the server administrator reaches public addresses only. Through a proxy. HttpProxy (host:port or [ipv6]:port, since 6.2.28) carries the update feed and its downloads, the JWKS and introspection fetches, the breach check and the object store. It sends no proxy credentials, and a value with no port fails with HttpProxy must be host:port (or [ipv6]:port). rather than going direct. ACME issuance, the outbound relay's token request, a mail-in-to-webhook route and the MTA-STS policy fetch never use it; the MTA-STS fetch is on by default, so a proxy-only egress stops it. Whether the other requests above use the proxy is not documented: prove a proxy-only egress before relying on it. UpdateFeed.md, written before the feed site's first deployment, says the address answers nothing usable until then. The firewall matrix Direction Port From or to Only when In TCP 25 Anywhere Always In TCP 587, and 465, 993, 995 Anywhere, or your users' networks 587 always; the others once you have created them In TCP 143, 110 Your users A client still uses them; deny them afterwards In TCP 80 Anywhere ACME, or web services over HTTP In TCP 443 Anywhere WebServicesHttpsPort is 443 In TCP REST port (8045) Your management network; users for /portal; the identity provider for SCIM The listener is bound beyond loopback, with TLS In TCP metrics port, ManageSieve 4190 Prometheus; your management network Bound beyond loopback (or keep 127.0.0.1 and a tunnel) In UDP 161 The SNMP managers only, on the bound address The agent is on In TCP 135 and DCOM The administrator's machine A remote Control Panel (Windows) Out TCP 25 Anywhere, or only the smart host on its port Always Out UDP and TCP 53 Your resolvers Always Out TCP 443 Anywhere, or the hosts in the tables above MTA-STS is on (the default) or a feature needs it Out Database, spamd, clamd, syslog, OTLP ports Those hosts They are not on this machine Out TCP 110, 995, 143, 993, or the account's port The servers mail is collected or migrated from An external account or a migration exists Windows. The installer opens no firewall port, so nothing reaches the server until you add the rules. In an elevated PowerShell: New-NetFirewallRule -DisplayName "hMailServer SMTP" -Direction Inbound -Protocol TCP -LocalPort 25 -Action Allow New-NetFirewallRule -DisplayName "hMailServer Submission" -Direction Inbound -Protocol TCP -LocalPort 587 -Action Allow New-NetFirewallRule -DisplayName "hMailServer IMAPS" -Direction Inbound -Protocol TCP -LocalPort 993 -Action Allow New-NetFirewallRule -DisplayName "hMailServer POP3S" -Direction Inbound -Protocol TCP -LocalPort 995 -Action Allow Linux. With firewalld, firewall-cmd --permanent --add-port={25,587,993,995}/tcp and firewall-cmd --reload; with ufw, ufw allow 25,587,993,995/tcp. The documentation page's chapter 4.4 also opens 110, 143 and 465; leave them out. On either system, add 143, 110, 465, 80 or 443 only when the matrix says so, and forward the same ports on the router or cloud firewall. The Linux package's own firewall hook (AutoBanFirewall=1) only blocks, and needs CAP_NET_ADMIN, which the unit does not grant by default; on Windows the same switch is refused under NT SERVICE\hMailServer (HM7460). Put the addresses you administer from in AutoBanNeverBan first; Reading the logs, and fail2ban has auto-ban in the firewall. What it does not do The SNMP agent sends no trap or inform and does not answer SNMPv1; nothing leaves it unasked. Country blocking downloads no geographic database; the .mmdb file is yours to fetch and keep fresh. No AI or machine-learning service is called by the server. Where to read more Wiki: Ports Reference, Before You Install, Installing hMailServer, Installing on Linux, Settings Reference, Troubleshooting, Encryption and Certificates. Documents at v6.3.4: EvidencePack.md (what leaves the server), SettingsApply.md, Snmp.md, UpdateFeed.md, InboundWebhooks.md, ObjectStorage.md, PasswordBreachCheck.md, Migration.md. The documentation page's ports reference and Open the firewall chapters. On the forum: Let's Encrypt certificates are automatic, First install on Linux, The Control Deck, Installing on Windows: wizard pages, service account, profile and the first hour, Your first domain and mailbox, and the DNS records your domain needs, TLS on the listeners: your own certificate, versions, ciphers and Troubleshooting by symptom: no mail in, no mail out.
  • 0 Votes
    1 Posts
    170 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) changes what an upgraded server does without anything being switched on, and some of it looks like a fault. A forward, a redirect or a rule's forward of mail from outside that did not pass DMARC for the forwarding domain's organisation keeps the envelope sender it came with, with no SRS address or rewritten sender; where the copy would still carry an identity of that organisation (an empty envelope sender under this server's host name, or an envelope sender at this server's host name or at a hosted domain in that organisation) it is not sent to an outside address at all: the message stays in the mailbox, and the application log names each recipient. An authenticated submission is signed as another hosted domain only when the account may send as the address its From names, and is otherwise sent unsigned as that domain. A domain's or an account's rule may no longer set From, Sender, Resent-From, Resent-Sender, DKIM-Signature or the loop count, and a Sieve script may no longer add or delete them: one stored before the upgrade still runs without that action, and the application log names it. RuleLoopLimit 0 stops out-of-office replies. A delivery whose mail servers' DNS lookup failed tells the sender that the DNS query failed (4.4.4) and retries. On Windows the x64 installer's MariaDB Connector/C 3.4.11 has seven authentication plugins where 6.3.6's had nine, so a database account that signs in with MySQL Enterprise's Windows authentication can no longer sign in and the server cannot open its database. An error log that was quiet may now name database failures that were hidden (HM5032), and the Control Panel's API & monitoring page is Monitoring & alerts. Fixed from reports: the Control Panel's IP ranges page that could show no list at all, DANE enforcement deferring mail to a DNSSEC-signed domain whose MX answer came compressed (live.com among them), and classic Outlook's POP3 downloads stopping at the UTF8 it sends after the login. See the 6.3.7 announcement. After the upgrade to hMailServer 6.3.8 (2 November 2026), TlsMode=verify-full on MySQL or MariaDB with no TlsCaFile set is refused with HM7535 on a connection to a database server on this machine, and the database is not reached; on Windows a TlsCaFile or PostgreSQLSslRootCert whose path holds characters outside ASCII may be refused with HM7534, with the same result, and a TLS certificate or key file so named with HM7536, so that the port it is for does not start; and a Linux server that ran under a LANG that is not UTF-8 no longer finds a file or folder named outside ASCII until it is renamed to its UTF-8 name. A client that signs in with another spelling of the account's address than the stored one, in more than the case of the letters A to Z, or with white space at either end, may no longer sign in. With the web services listener on, an address whose IP range allows none of SMTP, POP3 and IMAP - an auto-ban's, or one of your own that allows nothing - is closed there with no answer, and with it CalDAV, CardDAV, Autodiscover, autoconfiguration, MTA-STS and, when this listener answers it, ACME's http-01 validation. See the 6.3.8 announcement. This topic is the wiki's Troubleshooting chapter arranged by the sentence support threads open with: mail from outside does not arrive, a client cannot send, the Control Panel or the Deck will not connect, the service will not start. It also covers the server's replies, the Diagnostics self-test, the message trace and what only looks like a fault. The one thing to know first: on Windows the service tells the Service Control Manager it is running before it has opened its database or a single port, so "the service is running" proves nothing. Today's ERROR_hmailserver_<date>.log does: it almost always holds the reason a start failed. Start here Three pages, in this order, under Monitoring & troubleshooting in the Control Panel (Windows): Diagnostics, the built-in self-test (below). It names most problems outright. Live logs, while you reproduce the problem, with debug messages on if the answer is not obvious, and off afterwards. Delivery queue: is mail stuck, and what do NextTry and Tries say? On Linux the Deck's Diagnostics, Logs and queue views read the same, and three commands start every investigation: systemctl status hmailserver journalctl -u hmailserver -n 100 --no-pager sudo tail -n 100 /var/log/hmailserver/ERROR_hmailserver_$(date +%F).log Which problem is it? Not running, or nothing listening: The service will not start. A sender got an SMTP error: the reply names the cause. A sender timed out, or outbound mail sits in the queue: Diagnosing stalled mail. Inbound with nothing at all: I can't receive. Outbound that never reaches the queue: I can't send. "I can't receive mail from outside" Work down the table in order, cheapest check first. Check How (Windows) A failure means Is the service running? Get-Service hMailServer Start it, then read the error log for why it stopped Is anything listening on 25? Get-NetTCPConnection -LocalPort 25 -State Listen The port is not configured, is bound to one address only, or another program has it: Connections & protocols → TCP/IP ports Is 25 open in Windows Firewall? Get-NetFirewallRule -Enabled True \| Where-Object DisplayName -like "*hMail*" Open it: see the firewall paragraph below Does the machine itself answer on 25? Test-NetConnection 127.0.0.1 -Port 25 Then it is the server, not the network Is 25 forwarded by your router or cloud firewall? Its configuration The far end sees a refusal or a timeout Does your ISP block 25? Ask them Common on consumer lines: a relay host or a business line Is the MX record right? The MX query page, or Resolve-DnsName example.com -Type MX Fix DNS Can the world reach you? From outside: Test-NetConnection mail.example.com -Port 25 It is the path in Is the domain in hMailServer and Active? Accounts & domains → Domains An inactive domain is not accepted for Is the mailbox Active, and full? The account dialog A full mailbox gets a temporary failure, and the sender retries A port bound to one internal address answers only on the LAN. Server status lists the listeners the service actually brought up. The firewall. The installer opens no port, so nothing from outside reaches the server until you open them, on the machine and on the router or cloud firewall in front of it. Which ports, and the rules for Windows and Linux, are in Every port hMailServer listens on or connects to. "I can't send mail" The reply names most sending faults. In an ESMTP session an enhanced status code follows the number: 530 Must issue STARTTLS first. arrives as 530 5.7.0 Must issue STARTTLS first. Reply Likely cause What to do 550 Delivery is not allowed to this address. ("relay denied") The client is not authenticating, or uses 25 instead of 587 Port 587 with authentication; a printer that cannot authenticate gets its own range under Access & abuse protection → IP ranges 530 SMTP authentication is required. The same, said earlier As above 530 Must issue STARTTLS first. The port requires TLS first STARTTLS or SSL/TLS in the client 535 Authentication failed. Wrong password, not the full address as user name, or an inactive account Also check the auto-ban 535 Authentication failed. Too many invalid logon attempts. The auto-ban: 3 failures, 60 minutes, by default Below 550 Too many recipients. Too many recipients in one transaction Below 552 5.3.4 Message size exceeds fixed maximum message size. Size: N KB, Max size: M KB Over the server's maximum message size (20480 KB), or the domain's own Connections & protocols → Protocols, SMTP tab, or the domain dialog 452 4.3.1 Insufficient system storage. Free space on the store's volume below MinimumFreeDiskSpaceMB (100 MB) Free space; senders retry A banned client. The ban is an expiring IP range: Access & abuse protection → IP ranges lists it, and deleting it releases the client (DELETE /api/v1/ipranges/<id>, or the Deck's IP ranges view). 550 Too many recipients. Two wiki pages disagree. Troubleshooting §19.3 ties it to maxsmtprecipientsinbatch (100) and says split the batch or raise the limit; the Architecture page's RCPT TO chart refuses at 50,000 recipients and uses MaxSMTPRecipientsInBatch to batch outbound deliveries. Splitting the message is right either way; raising the setting also changes the outbound batch size. What the server's replies mean A 4xx is temporary and the sender retries; a 5xx is permanent and the sender bounces. Reply Meaning 421 4.7.0 Too many invalid commands. Bye! Disconnected for misbehaving: a scanner, or a broken client 451 4.3.1 Server temporarily overloaded while accepting the message; please retry. The accept pipeline exceeded FinalizationTimeout: Diagnosing stalled mail 451 4.3.2 Unable to verify the recipient at the moment. Please retry later. The database did not answer, so the mail is deferred, not bounced. Check /readyz 451 4.3.2 The server is shutting down and cannot accept the message. A graceful stop with ShutdownDrainSeconds set 452 4.2.2 Mailbox is full. At or over quota, refused during the conversation; the holder had a QUOTA_WARNING 550 Unknown user No such mailbox, alias or list 553 or 554 from a remote server The far end refused you; its text is the diagnosis, usually SPF, DKIM, PTR or a block list "My mail goes to spam" Almost always DNS, in this order: the PTR record (nothing compensates for a missing or generic one), SPF, DKIM, DMARC even at p=none, then the block lists. With split-horizon DNS, the outside view is the one that counts. Being trusted by the servers you send to covers it. The Control Panel, the Deck or the REST listener will not connect The Control Panel (Windows) talks to the running service over COM: no service, no connection. Use the administration password, not a mailbox's. After a restart it reconnects by itself; after you stop the service it deliberately does not. Remotely it needs port 135 and DCOM. Every page saying "Server unavailable" means the engine is paused: Server status → Resume. Since 6.3.4, under NT SERVICE\hMailServer, a change of the administrator password is refused because the service may not write the file: run hMailServer.exe /SetAdministratorPassword from an elevated prompt, then restart the service. More in The Control Panel on Windows. The Deck and the REST API share one listener, off until RestApiPort is set. It refuses to start without an administrator password, beyond loopback without a certificate, or with a certificate it cannot load (HM5113 names the file), each logged once as RestApi: Refusing to start; the three lines and their fixes are in The REST API: turning it on. Only a RestApi: Listening on line proves it is up. Since 6.3.4 the port waits for a reinitialise (Apply now, or POST /api/v1/server/reinitialize), and a line edited into hMailServer.ini after the first start loses to the stored value (HM5804). With the listener off, on Linux: sudo hmailserver --set-setting RestApiPort=8045 sudo systemctl restart hmailserver On Windows, the Control Panel's API & monitoring page (Monitoring & alerts since 6.3.7). "Web administration page not installed" means the WebAdmin files are missing, or ProgramFolder does not point at the directory holding them. Repeated bad passwords get the address refused for five minutes; loopback is exempt. The Deck itself is The Control Deck. The service will not start The start, in order; where it stops names the problem: Windows is told the service is running, before any of what follows. hMailServer.INI is read, alone, which is how the database is found. A [Database] Type it does not recognise abandons the start, with nothing logged about a database. On Linux, sudo -u hmailserver hmailserver --check-config then prints Database type: 0. HM5005: Server or Database is missing for the configured Type. The database is connected, retried ConnectionAttempts times (6), ConnectionAttemptsDelay seconds apart (5). HM4354: the database server is down or moved, or the credentials changed. On Windows the [Database] password is DPAPI-protected per machine and cannot be copied from another server. Since 6.3.4, HM7530 (an unknown TlsMode) and HM7531 (verify-full with a missing TlsCaFile) stop it too, and since 6.3.8 HM7535 (verify-full on MySQL or MariaDB with no TlsCaFile set, to a database server on this machine: name the CA that issued its certificate in TlsCaFile, or set TlsMode=require) and, on Windows, HM7534 (a TlsCaFile or PostgreSQLSslRootCert name the client cannot be handed as named: give the file and the folders above it ASCII names); --check-database makes one connection and says what it got. The schema version, compared both ways. HM5010: Database version could not be detected. HM5011 too old: DBUpdater.exe from Bin on Windows, sudo -u hmailserver hmailserver --upgrade-database on Linux. HM5011 too new: install the matching or a newer server; never downgrade the database. Since 6.3.4, [Database] UpgradeOnStart=1 makes the service upgrade the schema as it starts: off by default, no backup first, one node at a time. Settings are read again from the database, since 6.3.4 with any [SettingsOverride] keys over them. A configuration that will not load abandons the start. One listener per TCP/IP ports row. A row whose protocol is off on Protocols → Services is skipped in silence, the commonest reason for nothing on 143. HM4316: something else holds the port (IIS on 25 or 443, another mail server, a leftover instance); that port alone stays down. HM5113 with Error initializing SSL. Certificate not set.: that listener does not start. The optional listeners (metrics, REST, web services, ManageSieve), each only with a non-zero port. MetricsServer: Invalid bind address means MetricsServerBindAddress is not an IP literal (localhost is refused too); that listener stays down and the server runs. A failure in steps 2 to 6 stops the whole server, and one with a code writes it to the ERROR log; steps 7 and 8 cost one listener and go to the application log. Windows. A service that stops at once with nothing logged is the account's rights on Bin, Data, Logs and Database. Since 6.3.4 a new installation runs as NT SERVICE\hMailServer (an upgrade keeps its account), and a folder outside the program folder needs approving: run hMailServer.exe --confirm /GrantServiceAccess elevated in Bin, and approve only a folder you recognise, since the service may then write in it. Local SQL Server with integrated authentication needs a login for NT SERVICE\hMailServer. What the account may not do, and moving between it and LocalSystem, are in Installing on Windows. The installer's recovery settings restart a failed service after 1, 2 and 5 minutes (sc.exe qfailure hMailServer). Linux, under systemd. A start that fails before any listener opens puts hMailServer could not start: in the journal, with the reason on standard error in case the log directory is the fault. The unit runs as hmailserver, and ProtectSystem=strict makes anything outside its ReadWritePaths (/var/lib/hmailserver and /var/log/hmailserver) read-only to the service: a data, backup, archive or ACME directory elsewhere needs a drop-in (sudo systemctl edit hmailserver). HM5094: the MySQL or MariaDB client library is not installed. HM5603 on every TLS listener: /usr/bin/dh2048.pem is missing; reinstall the package. The Diagnostics self-test Monitoring & troubleshooting → Diagnostics in the Control Panel; since 6.3.4 also the Deck's Diagnostics view and POST /api/v1/diagnostics/run, on either platform. Ten tests: Test Fails when Server details Never; since 6.3.4 it also says whether the database connection is encrypted Installation paths (6.3.0) A configured path does not exist Test IPv6 Never Test outbound port Outbound 25 is blocked, or the smart host's name, port or TLS mode is wrong Test backup directory No destination, or the service cannot write there Test MX records Public DNS is wrong, or the resolver is not answering Test local connect Hairpin NAT fails, 25 is not forwarded, or the MX points elsewhere Test message file locations Files outside the data directory, or full paths stored, which stops the built-in backup Test IP range configuration 127.0.0.1 has been auto-banned Error logs Any ERROR* file exists Fill both domain boxes first: the MX and local-connect tests, and the outbound test without a smart host, are skipped rather than failed when they are empty. Copy puts the result on the clipboard for a post. "Did we ever get it?": the message trace The message trace records what the server did with each message, keyed on its queue id. It is off by default (MessageTraceEnabled), because it records who corresponds with whom, and nothing is recorded while it is off: turn it on before you need it. Turning it on. Since 6.3.4 it applies when saved: the Control Panel's Message trace card on Monitoring & troubleshooting → Logging, PUT /api/v1/settings/ini/{name}, or hmctl, both with the administrator password: hmctl ini set MessageTraceEnabled 1 On 6.3.3 and earlier it is MessageTraceEnabled=1 under [Settings] and a restart. Rows are kept MessageTraceRetentionDays (30): since 6.3.4 an hourly task deletes older ones; on 6.3.3 and earlier only Remove expired on the Message trace page, or MessageTrace.DeleteExpired over COM, did. Reading it. The Control Panel's Message trace page searches an address as sender or recipient, newest first, and Follow this message shows one queue id. Over REST since 6.3.4: GET /api/v1/message-trace, /api/v1/message-trace/address/{address} and /api/v1/message-trace/queue/{id}, which need the view_message_trace permission. hmctl api GET /api/v1/message-trace/address/[email protected] What a row says. One row per recipient per delivery attempt: when, the event, sender, recipient, source address and status code. Events include delivered and failed; on 6.3.4 also deferred (an attempt that will be retried) and suppressed (stopped by the suppression list). A refusal at RCPT carries queue id 0. A forward, a list or a rule that re-sends makes a new queue entry. Answering the question. A delivered row: you had it; look at the account's folders and rules. A row with queue id 0: refused at RCPT, and the status says why. No row: either it never reached you, or it is still queued or held, came while the trace was off, or is past the retention. The trace records delivery attempts only, with no acceptance, spam or virus event, so an empty trace does not prove the server never had the message. The per-domain reports' traffic, failures, senders and recipients are counted from it, and say so while it is off: Alerts, the audit trail and per-domain reports. Things that look like faults and are not What you see Why A new sender's first message is minutes late Greylisting doing its job (6.3.4) A value typed into [Settings] is back after a restart, HM5804 in the ERROR log The database is the settings store: set it in the Control Panel, over REST (PUT /api/v1/settings/ini/{name}) or with hmctl ini set (6.3.4) HM5807 at every start A [SettingsOverride] section is forcing keys; remove it once the stored value is right (6.3.4, Linux package) HM5803 at every start The service cannot write its copy into the root-owned file; the stored value is in effect 6.2.28 to 6.3.3: "The update feed answered HTTP 404." Set UpdateFeedUrl=https://updates.progressiverobot.com/releases.json and restart "Release 6.3.4 carries no Sigstore bundle for its installer, so it cannot be verified." Install 6.3.4 by hand: Upgrading to 6.3.4 Where a message waits, since 6.3.4. Neither delivered nor queued may mean held on purpose: by the sandbox, by compromised-account detection, in a back-off window, under an IP warm-up ceiling or behind a drain; Diagnosing stalled mail says where to look. And 550 5.7.1 Too many delivery attempts before the greylisting delay had passed is greylisting's abuse lock, lifted after GreyListingAbuseLockMinutes (60); a 5.7.10 bounce to a partner domain is gateway encryption refusing to send in the clear, the partner's key having expired. What it does not do It does not diagnose a mail client (Connecting a mail client), read logs line by line (Reading the logs), or work a stalled queue. The Control Panel and COM are Windows-only. When nothing here names the fault, Read this before you post says what a request needs. Without a GitLab account, email the Service Desk, contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, which GitLab files as a confidential issue. Where to read more Wiki: Troubleshooting, Ports Reference, Monitoring and Health, Installing hMailServer, Installing on Linux, The Control Panel, The REST API, Upgrading hMailServer, Architecture. Documents: ServiceAccount.md, DatabaseTls.md, Reports.md, SettingsApply.md. On the forum: Settings: when a save takes effect (HM5804 and the reinitialise), IP ranges, auto-ban and lockout, Choosing a database and encrypting the link (TlsMode, HM7530 and HM7531) and The hmailserver command line on Linux (the unit and its drop-ins). Public documentation: Troubleshooting and Troubleshooting a Linux installation.
  • 0 Votes
    1 Posts
    134 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) changes some of what a client gets. IMAP COPY and MOVE by message number read their set in the client's numbering, and a set that names a message another session has just expunged is refused NO [EXPUNGEISSUED] for the client to send again; a COPY or MOVE that fails part way leaves the destination as it was; SEARCH, SORT and THREAD ask a criterion named again beside itself once; and FETCH BODY[N.MIME], and BODY[N] of a multipart, are the stored octets. POP3 accepts the UTF8 classic Outlook for Windows (2016 to 2024) sends after the login, until the session's first STAT, LIST, UIDL, RETR, TOP, DELE or RSET, so its downloads work again. SMTP, POP3 and ManageSieve refuse a SASL PLAIN sign-in that asks to act as another account. The IMAP and POP3 listeners can take the client's address from a PROXY protocol header, as the SMTP listener does, each from the peers on a list of its own, both off by default. The send-as grant now bears on DKIM too: an authenticated submission is signed as another hosted domain only when its account may send as the address its From names - its own, an alias pointing at it, a mailbox that granted it the post right, an address it proved, or a masked address of its own - and is otherwise accepted and sent unsigned as that domain. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) signs a name in to an account only when it is the account's stored address, compared in lower case after a domain alias and the default domain, on IMAP, POP3 and SMTP alike, SCRAM-SHA-256 among them: a client that signs in with another spelling of the address, in more than the case of the letters A to Z, or with white space at either end, may no longer sign in, since such a name fails as a name that does not exist fails and is counted as a failure. Correct the name in the client. See the 6.3.8 announcement. This topic is the server side of IMAP, POP3 and SMTP in hMailServer 6.3.4: what each listener advertises and which switch gates it, search limits and the full-text index, QRESYNC's expunge history, connection limits, shared mailboxes, and what 6.3.4 fixed that a client may notice. Know this first: a capability missing from an answer is nearly always a switch or the session's state, not a fault. Two IMAP switches ship off, and since 6.3.4 IMAP offers STARTTLS only before TLS and sign-in, and COMPRESS=DEFLATE only after sign-in. What a mail client needs is in Connecting a mail client; the ACL rights table is in Aliases, distribution lists, mailing lists and public folders. Seeing what the server advertises Ask the server, from any machine with OpenSSL. For SMTP type EHLO test.example.org; for IMAP, CAPABILITY: openssl s_client -connect mail.yourcompany.com:25 -starttls smtp -quiet openssl s_client -connect mail.yourcompany.com:993 -quiet On 6.3.4 ask IMAP before LOGIN and after: the answers differ by design. For POP3, connect to 995 the same way and send CAPA. Ports 993 and 995 exist only once you have created them (Connections & protocols → TCP/IP ports in the Windows Control Panel, or the Control Deck). IMAP: what is always there On every connection, as in 6.3.3: IMAP4 IMAP4rev1 IMAP4rev2 CHILDREN THREAD=ORDEREDSUBJECT THREAD=REFERENCES NAMESPACE RIGHTS=texk MOVE ID SPECIAL-USE CREATE-SPECIAL-USE UNSELECT UIDPLUS ENABLE STATUS=SIZE ESEARCH CONDSTORE QRESYNC LIST-EXTENDED SEARCHRES WITHIN UTF8=ACCEPT LIST-STATUS PREVIEW MULTIAPPEND REPLACE SAVEDATE METADATA OBJECTID BINARY LITERAL- APPENDLIMIT APPENDLIMIT is bare before authentication and APPENDLIMIT=<bytes> after. LITERAL- caps non-synchronising literals at 4096 bytes, so an oversized APPEND can still be refused with TOOBIG. A client opts in to IMAP4rev2 per session with ENABLE IMAP4rev2, which implies UTF8=ACCEPT. ENABLE recognises exactly four names: QRESYNC (implies CONDSTORE), CONDSTORE, UTF8=ACCEPT and IMAP4rev2. After ENABLE IMAP4rev2 the session drops the RECENT response and the [UNSEEN] code on SELECT and EXAMINE, drops RECENT as a STATUS item, and answers a SEARCH with no RETURN options as ESEARCH. IMAP: what is gated, and by what Capability Advertised when Shipped IDLE enableimapidle is on On QUOTA QUOTA=RES-STORAGE enableimapquota is on On SORT enableimapsort is on On ACL enableimapacl is on; it also switches the #Users namespace On AUTH=PLAIN, AUTH=SCRAM-SHA-256 EnableImapSASLPlain is on and authentication is available Off SASL-IR EnableImapSASLInitialResponse is on Off COMPRESS=DEFLATE IMAPCompressionEnabled=1, the client has signed in (since 6.3.4), and the session is not yet compressed On STARTTLS The port is STARTTLS-optional or -required, TLS is not active, and (since 6.3.4) the client has not signed in Per port LOGINDISABLED Authentication is refused on this cleartext connection (STARTTLS-required port, or RequireTLSForAuth on the IP range) Per range The rest depend on the connection: AUTH=SCRAM-SHA-256-PLUS only on TLS (and behind the same SASL switch), AUTH=XOAUTH2 AUTH=OAUTHBEARER with OAuth2Enabled and, unless OAuth2RequireTLS=0, only over TLS, AUTH=EXTERNAL on a verified client certificate that names an address, and UNAUTHENTICATE once signed in. IDLE reports changes in the selected folder (EXISTS, RECENT, EXPUNGE, FLAGS) as they happen. A folder another client has just created is seen at the next folder-list refresh, as there is no IMAP NOTIFY; that is IMAP, not a fault. QUOTA answers with one unnamed root, "", and a STORAGE resource in kilobytes from the account's maximum size; an account with no limit gets an empty list. SETQUOTA is refused, because the quota is the administrator's. Refusals of APPEND, COPY and MOVE for a full mailbox carry OVERQUOTA. SORT: with message indexing off, as it ships, SORT reads each message's header from disk. EnableImapSASLPlain gates SCRAM as well as PLAIN, despite its name. Roadmap.md says it gates the whole AUTHENTICATE command, while the Capabilities wiki gives OAuth2 and EXTERNAL conditions of their own: with it off, test XOAUTH2, OAUTHBEARER and EXTERNAL over IMAP before relying on them. COMPRESS DEFLATE (RFC 4978) compresses both directions of the rest of the session; a client that does not ask is unaffected. A second one is NO [COMPRESSIONACTIVE], STARTTLS under compression is BAD, and IMAPCompressionEnabled=0 withdraws it, the command then answering BAD like an unknown one. POP3 and SMTP: the same question POP3 CAPA always carries UIDL, TOP, RESP-CODES, AUTH-RESP-CODE, IMPLEMENTATION hMailServer, PIPELINING, UTF8 and EXPIRE NEVER. The SASL mechanisms depend on TLS, STLS on the port, and LOGIN-DELAY <n> appears only when Pop3LoginDelaySeconds is above 0 (0 ships). That key advertises and enforces a minimum interval between sign-ins, refusing an early one with -ERR [LOGIN-DELAY] …; on Windows it is the POP3 tab's card How often a client may check for mail. SMTP EHLO always carries SIZE <n> (the maximum message size in KB times 1024; bare SIZE when 0), 8BITMIME, PIPELINING, CHUNKING, BINARYMIME, SMTPUTF8, ENHANCEDSTATUSCODES, DSN and HELP. BINARYMIME is for local delivery: a recipient needing onward relay is refused at RCPT TO with 554 5.6.3. Conditionally: XCLIENT only when SMTPXClientEnabled=1 and the real peer is in SMTPXClientTrustedIPs; an untrusted peer that tries it gets 550. STARTTLS on a cleartext connection to a STARTTLS-optional or -required port. AUTH only when authentication is enabled, withheld on cleartext when the port requires STARTTLS or the range sets RequireTLSForAuth, and never on the ports in DisableAUTHList. Not a keyword, but decided the same way: the HAProxy PROXY protocol is accepted only with SMTPProxyProtocolEnabled=1 and from a peer in SMTPProxyProtocolTrustedIPs, and nothing in EHLO announces it. On SMTP and POP3 the AUTH line and its SASL responses take up to 12,288 octets; every other line keeps 510 (SMTP) or 500 (POP3). What 6.3.4 added to SMTP is refusals, not capabilities. Where the switches are set, and when a change applies What Windows Control Panel Elsewhere Applies IDLE, QUOTA, SORT, ACL, the two SASL switches, the connection limits Connections & protocols → Protocols, IMAP, SMTP and POP3 tabs COM Settings (IMAPIdleEnabled and its neighbours); REST PUT /api/v1/settings (imap_idle_enabled, imap_quota_enabled, imap_sort_enabled, imap_acl_enabled, imap_sasl_plain_enabled, imap_sasl_initial_response_enabled); the Deck's Settings view; hmctl settings set At once Message indexing Maintenance → Performance, Indexing tab COM Settings.MessageIndexing; PUT /api/v1/settings/indexing (enabled); hmctl settings, group indexing At once IMAPCompressionEnabled, IMAPSearchTimeout, IMAPSearchMaxMegabytes, IMAPExpungeRetentionRecords, Pop3LoginDelaySeconds, IndexerFullText and its tuning keys Cards on the IMAP and POP3 tabs; the Indexing tab COM Settings.SetIniSetting; PUT /api/v1/settings/ini/{name} (administrator password only); hmctl ini; on Linux hmailserver --set-setting NAME=VALUE. No Deck page When saved The Control Panel and COM are Windows-only. The last row's keys were the [Settings] section of hMailServer.ini; since 6.3.4 the database holds them, none of them waits for a reinitialise or a restart (docs/SettingsApply.md), and the server does not take a line edited in the file, so set them through an interface. Settings: when a save takes effect covers the store, HM5804, and a value stored by --set-setting while the service runs. The REST lines assume the listener on 127.0.0.1:8443, as the REST API wiki's examples do; RestApiPort ships at 0 (off) and waits for a reinitialise. # Switch IDLE off; in effect when the answer comes back curl -u Administrator:your-admin-password -X PUT -H "Content-Type: application/json" \ -d '{"imap_idle_enabled":false}' http://127.0.0.1:8443/api/v1/settings # A [Settings] key: the administrator password only, never an API key curl -u Administrator:your-admin-password -X PUT -H "Content-Type: application/json" \ -d '{"value":"120"}' http://127.0.0.1:8443/api/v1/settings/ini/IMAPSearchTimeout # On Linux, then a restart so the running server takes it sudo hmailserver --set-setting IMAPSearchTimeout=120 sudo systemctl restart hmailserver Search: two ceilings and the full-text index Without the full-text index, SEARCH BODY and TEXT read every message in the mailbox, attachments whose media type is text included (decoded, bounded at 1 MB). Two ceilings, measured from the moment a command starts and never re-armed by progress, bound SEARCH, SORT and THREAD: IMAPSearchTimeout (60 seconds) and IMAPSearchMaxMegabytes (2048 MB of message content read); 0 turns either off. They are the IMAP tab's card IMAP search limits, and README.md says to raise them for mailboxes of several hundred thousand messages. What a client gets at a ceiling is stated two ways. Roadmap.md records NO with RFC 9051 LIMIT and no untagged result, so a cut-short list cannot pass for a complete one; README.md's comment beside IMAPSearchTimeout says the search returns the matches found so far. Until that is settled, treat a search that hit a ceiling as incomplete, and let nothing act on its result in bulk. The full-text index is off, and needs two switches: IndexerFullText=1 indexes nothing unless message indexing (Settings.MessageIndexing) is on too. It costs a term table of a few kilobytes per message and a backfill pass over every message already delivered, which is why no upgrade turns it on. The index can only prove that a message cannot match; everything else is read and scanned as before, so results are identical on or off. The webmail's search and eDiscovery use the same filter. Key Default What it does IndexerFullTextBatchSize 250 (1 to 100000) Messages one backfill pass reads before the cursor is saved and the thread pauses IndexerFullTextMinTokenLength 3 (3 to 64) The shortest search string the index answers for IndexerFullTextMaxTokensPerMessage 2048 (64 to 1000000) Distinct terms before a message is marked always-scanned instead of indexed The Indexing tab's Index now, and show how far behind it is and Discard the index and rebuild it are POST /api/v1/settings/indexing/index and /clear over REST, and Index() and Clear() over COM, where TotalIndexedCount against TotalMessageCount shows a rebuild's progress. With mailboxes encrypted at rest, the indexed count stays short by their messages for good: those are never indexed. QRESYNC: how much expunge history is kept IMAPExpungeRetentionRecords keeps the newest 5000 expunge records per mailbox (0 keeps all), pruned every twelve hours and once at service start; on Windows it is the card Change history for synchronising clients. The newest is never removed. A client whose last sync is older than what is kept gets a full resync, never a wrong one: every UID in its range that the mailbox does not hold. An installation upgraded from a release older than 6.2.23-alpha1 prunes the table in one pass at its first start. README.md says 0 defers that prune, and an IMAPExpungeRetentionRecords line of 0 added to [Settings] before the upgrade is taken: 6.3.4's first start adopts from the file a key the database has never held, and before 6.2.23-alpha1 the database held this one only if the file had the line. Restart the old service once after adding or changing it (Upgrading hMailServer wiki, 18.3, step 6): CHANGELOG.md's note that a line edited to prepare an upgrade is put back concerns a key the database already holds, and that restart settles it. Set it back to 5000 afterwards through an interface; Upgrading to 6.3.4 has the rest of the order. Connection limits and what guards them MaxSMTPConnections, MaxIMAPConnections and MaxPOP3Connections are on the Protocols page (REST max_smtp_connections, max_imap_connections, max_pop3_connections). None has a limit by default; SMTP and IMAP ship at 0, no cap. The Services tab turns a protocol off altogether (ServiceSMTP, ServiceIMAP, ServicePOP3). Watch hmailserver_sessions{protocol} on /metrics (off until MetricsServerPort is set, which waits for a reinitialise) or sessions in GET /api/v1/status. Around the limits: auto-ban counts failed sign-ins per IP across SMTP, IMAP, POP3, ManageSieve and REST (on: 3 in 30 minutes, a 60-minute ban); LogonTarpitSeconds (off) delays each failed sign-in's refusal; a second POP3 session on one mailbox gets -ERR [IN-USE]; and since 6.3.4 an IMAP LIST or LSUB pattern over 8,192 characters, or more than 32 patterns in one LIST, is answered BAD. The submission rate limit and the tarpits are in IP ranges, auto-ban, lockout and tarpits; refusing a connection by country is in allow and block lists and country blocking. Opening a mailbox to a colleague Shared and delegated mailboxes are IMAP ACL (RFC 4314). The owner opens a folder to another account, and it appears to that account under the Other Users namespace as #[email protected]. No administrator step is needed: any account whose client speaks SETACL can do it, and the audit trail records no row for it. Leave ACL on. With it off, #Users stops existing (NAMESPACE advertises NIL), public folders are open to everyone, and the send-as grant is not honoured. The owner grants rights from an IMAP client with ACL support. An administrator can do it over REST: GET on /api/v1/accounts/{address}/folders gives the folder's id, and /api/v1/accounts/{address}/folders/{id}/permissions lists, adds, changes and removes entries (since 6.3.3; writes need a full key). On Windows, COM has IMAPFolderPermissions.Add. The grantee is an account, a group (/api/v1/groups, since 6.3.4), or Anyone, which is every authenticated account on the server. Grant l, or the folder does not exist for the colleague. r reads, s changes read state, i, t and e file and delete, and p on the INBOX is the send-as grant. Grant on each top-level folder needed. A right is decided at the nearest folder up the owner's tree that has any entries, and entries are not merged: a grant on INBOX reaches its sub-folders that have none of their own, not the Sent folder, and one entry on a sub-folder cuts it off from the parent's. Over SMTP the p grant counts only while SmtpAuthenticatedSenderCheck is on, and it ships off: until then nothing constrains an authenticated MAIL FROM, so any account can send as the mailbox, or as anyone else. Turned on, it applies to every authenticated session on the server; the addresses it then allows are in who may send as whom. In the colleague's client, a pane of subscribed folders shows only the ones the owner subscribed to; the webmail shows them under Shared by <owner>. What follows from sharing: \Seen is stored on the message, not per person: with s, a delegate marks a message read for everyone; with lr alone they leave no trace, and their client cannot track what they have read. Since 6.3.4 an APPEND into a #Users folder is charged to the owner's quota, as COPY and MOVE already were. A delegate reads a folder of a mailbox encrypted at rest only while the owner's key is open; otherwise a FETCH that reads the message answers NO [UNAVAILABLE], and copying out is refused. In the webmail a read-only share refuses a flag or a delete with 403, a message cannot move between one's own mailbox and a shared one, and the owner cannot grant access from the page. Since 6.3.4 a restore puts each folder's access grants back. Every LIST runs one more query while ACL is on. What 6.3.4 fixed that a client may notice Where Dovecot's imaptest found IMAP answering other than the RFCs say (\Seen without .PEEK, APPEND byte for byte, SORT and THREAD order, among others), Connecting a mail client has the list. The rest: What a client did What it gets since 6.3.4 Sent STARTTLS after LOGIN, or inside TLS BAD, and no STARTTLS in CAPABILITY Sent COMPRESS DEFLATE before signing in NO Appended over a compressed connection, data inflating past the 250,000-octet receive buffer The whole message, where it was stored cut short or never finished Read ENVELOPE with a backslash in Date, or BODYSTRUCTURE with a quote or backslash in a part's parameters A structure that parses: the date's backslashes left out; quotes and backslashes escaped, a control character sent as a space Fetched a message the server cannot read NO [UNAVAILABLE] for a closed key; a damaged one (HM7350) left out, the rest fetched and the command ending NO - not OK with an empty ENVELOPE Changed one message's flags from two sessions at once Both changes kept; STORE ... (UNCHANGEDSINCE n) tested in the same step Went on sending a MULTIAPPEND's later literals after a refusal, one over the maximum message size (and 1 MB) The connection closed with BAD [TOOBIG], where it was read whatever its size Fetched a message only an unreachable object store holds IMAP NO [UNAVAILABLE], POP3 -ERR [SYS/TEMP] (object storage is new in 6.3.4, and off) Two heap-corruption aborts are gone as well (two sessions on one message's keywords; LIST beside folder changes). Most of these were in 6.3.3 too. What it does not do No APOP or CRAM-MD5 on POP3, and THREADID is answered NIL. Delegation is not measured in Thunderbird or Outlook, and an Outlook IMAP profile never acts on the Autodiscover answer's delegated mailboxes; the #Users namespace is what works. Where to read more Wiki: Capabilities and Configuration, Settings Reference (9.12), Connecting a Mail Client, Aliases, Lists and Public Folders (13.3), Control Panel Reference (7.5), Security Hardening (17.5, 17.7), Changes Since 6.2.10 (4.3, 4.4). At the 6.3.4 tag: SettingsApply.md, SendAs.md, EncryptionAtRest.md, DeckParity.md, README.md, CHANGELOG.md (6.3.4, IMAP, POP3 and the webmail), Roadmap.md. The public documentation: 34.3 IMAP details worth knowing, 30.3 IMAP folders and permissions. On this forum: Upgrading to 6.3.4, The Control Deck, The REST API: turning it on, Message storage: object storage and encryption at rest, What runs out first: pools, connections, large folders and Monitoring and health. A client that does not get on with these answers is a bug: a GitLab issue or, without a GitLab account, an email to contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, filed as a confidential issue (SUPPORT.md). A vulnerability goes only by SECURITY.md's routes.
  • 0 Votes
    1 Posts
    139 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) moves the Control Panel's pages: Updates is a page of its own under Maintenance, the sidebar has Compliance and Virus scanning groups, API & monitoring is Monitoring & alerts, the PROXY protocol and XCLIENT are a card on Incoming relays, and Ctrl+K takes every old title and route to its page. A Control Panel signed in to a server on another computer now shows and saves the server's hMailServer.ini [Settings] values. The anti-virus action named Delete infected attachments only in 6.3.6's Control Panel is Remove all attachments and deliver the text in 6.3.7's. The installers carry the .NET Desktop Runtime 10.0.13, Microsoft's security release of 13 October 2026, on which the Control Panel runs, but install it only where no .NET 10 Desktop Runtime is installed at all: an upgraded server keeps the 10.0.12 that 6.3.4, 6.3.5 or 6.3.6 installed, so bring it to 10.0.13 or later through Microsoft Update or Microsoft's installer. See the 6.3.7 announcement. The hMailServer Control Panel (hMailCP.exe) is the Windows desktop administration application: a .NET 10 WPF program, x64 only, that talks to the server over the COM API. That is why it can administer a server on another machine over DCOM, and why there is none on Linux, where the Control Deck, the REST API and hmctl are the administration surfaces (hMailServer on Linux: what you get and what you do not). This topic covers what it writes and where, connecting, the 65 pages with the store behind each, and, since 6.3.4, the mark under each field that says when a saved value applies, the pending bar, the Levels tab and the help pane. The one thing to know first: since 6.3.4 the Control Panel writes every [Settings] key over COM into the database, even when it runs on the server itself, and a Control Panel with no connection says it cannot change such a setting rather than writing to the file, where a stored key's line is ignored in favour of the database's value. What it holds, and what it writes where The Control Panel holds no configuration of its own. Its per-user state (window bounds, theme, language) is under HKCU\Software\hMailServer\ControlPanel. The server is reached over COM. Files on the server (hMailServer.INI, hMailServerApiKeys.ini, the log folder, the event script, certificate and key files) are reachable only when the Control Panel runs on the server itself. Since 6.3.4 the database is the store of the [Settings] keys, and the file holds a copy (where each setting is kept, and what stays in the file). The Control Panel reads those keys over COM, falls back to the file only when it has no session, and writes them over COM only; a [Settings] line edited by hand in the file changes nothing, and the error log names it as HM5804. [Database], [Directories] and [LDAP] are still read from the file only, and [Database] and [Directories] are deliberately not reachable over COM. Since 6.3.4 a new installation runs the service as NT SERVICE\hMailServer; an upgrade keeps the account it had (the service account, and moving to or from it). hMailServer.INI is read-only to that account on purpose, so its copy of the settings goes stale there. For the Control Panel that changes four things, each refused with a sentence rather than failing quietly: What Under the virtual account Settings saved on any page Unchanged, since they are kept in the database; only PasswordPepper, kept in the file only, is refused The Administrator password card, and enrolling or removing the administrator's second factor Refused. For the password the sentence names hMailServer.exe /SetAdministratorPassword from an elevated prompt The data, log and temporary directories Refused: an administrator changes the file and approves the new folder for the service Creating or switching the database Refused before anything is made Maintenance → Server limits & expert settings carries a Windows service account card: what the service actually runs as, and three buttons, each run after the Windows elevation prompt: Grant access (hMailServer.exe --confirm /GrantServiceAccess), Move to NT SERVICE\hMailServer and Move to LocalSystem. The live update's apply and the auto-ban's firewall rules need LocalSystem, and are refused under the virtual account with a sentence. From 6.3.6 a service running as NT SERVICE\hMailServer downloads and verifies an update, and an administrator installs it with Install update on the Server status page of the Control Panel on the server, with one Windows prompt; on 6.3.4 and 6.3.5 Install update cannot do it, and the administrator runs the downloaded installer by hand. Connecting Field What to enter When it is wrong Server localhost, or the remote server's name or IP; an empty box means localhost A name that does not resolve, or does not answer on TCP 135, gets a sentence saying so. The check has a four-second budget, so a mistyped host is a pause, not a hang User name Administrator. A mailbox address also authenticates, but only a server administrator can read the settings "Authentication failed. Check the user name and password." The server never says which of the two was wrong Password The administration password set during installation The same message "hMailServer COM API is not registered on the target machine." means you reached the machine but hMailServer is not installed there. "Unable to reach the server (RPC unavailable)." is HRESULT 0x800706BA: firewall, DCOM permissions, or the service is not running. Two second factors, and they are not the same thing. The server-enforced one, whose secret is [Security] AdministratorTotpSecret in hMailServer.ini, protects every client of the administrator credential: this Control Panel, COM scripts, and the REST API through its X-hMailServer-OTP header. The older one, AdminTotpSecret under HKLM\SOFTWARE\hMailServer, protects this installation of the Control Panel on this machine and nothing else. The code is asked for once, never in a loop, because a wrong password and a missing code look identical from outside. hMailCP.exe /connect <host> <user> <password> signs in as the window loads; the password is on the command line, where anything that can list processes can read it. Connecting opens the Dashboard, not Welcome. COM sign-in is deliberately outside the auto-ban and the per-name lockout, so guessing an administrator's name over IMAP cannot lock that administrator out. The window and the tree Since 6.3.4 the Control Panel is drawn on one design system. The window has three rows: the title bar; a command bar holding the sidebar toggle, a search field that opens the Ctrl+K palette, a server status pill (Not connected, Connected, Reconnecting…, Connection lost), the theme toggle and the administrator's menu; then the sidebar beside the content. Below 1,000 pixels of window width the sidebar collapses to a rail of one glyph per group. What did not change is the part you may have scripted: the navigation map, every page title, tab header, setting caption and automation id (nav-<key>) are what they were. The sidebar holds 65 pages in eight groups in 6.3.4 (55 in 6.3.3), never more than two levels deep: Monitoring & troubleshooting, Accounts & domains, Mail flow & delivery, Spam & virus filtering, Connections & protocols, TLS & certificates, Access & abuse protection and Maintenance, with Welcome, Dashboard and About outside them. The ten pages new in 6.3.4 are Suppression list, Journaling and dual delivery, Audit trail, Discovery, Administrator roles, Remote domains, Allow and block list, Greylisting bypass list, Domain white and black list and Setting profiles. Search (Ctrl+K) answers three questions in one ranked list: a page name or any alias; what you are trying to do, from 97 outcome phrases such as "stop spam"; and a setting name or key, from an index of 448 settings in 6.3.4 (393 in 6.3.3). It also resolves 54 old navigation paths, so Settings > Security > Auto-ban & SSL/TLS opens Auto-ban. Welcome. Since 6.3.4 it offers a throwaway sample domain, example.test, and a first-run tour of seven stops that never acts and is never modal. Every page, by what it writes The wiki lists each page with its navigation key, which never changes when a page is renamed. Grouped by the store behind them, the 65 are: What the page writes Pages Applies Nothing: no settings behind the page (6) Welcome, Dashboard, Live logs, Diagnostics, MX query, About not applicable Read-only, or actions only (12) Server status, Delivery queue, Message trace, Audit trail, Discovery, External setup, DNS records, Server sendout, Spam filtering overview, Quarantine, Virus scanning overview, Transport encryption overview Actions at once The COM settings tree, a COM collection, or the database through COM (25) Diagnosing stalled mail, Suppression list, Journaling and dual delivery, Domains, Public folders, Groups, Administrator roles, Routes, Remote domains, Incoming relays, Rules, Server messages, SURBL servers, DNS blacklists, White list, Blocked senders, Allow and block list, Greylisting white list, Greylisting bypass list, Domain white and black list, Blocked attachments, TCP/IP ports, SSL certificates, IP ranges, Setting profiles At once; a profile's entries as each setting's own mark says [Settings] keys, in the database since 6.3.4 (7) API & monitoring, Web services & autoconfiguration, DNS resolver, Certificates (ACME), Transport security, Authentication, Server limits & expert settings As each field's mark says COM and [Settings] keys together (11) Logging, Delivery of e-mail, Anti-spam settings, Anti-virus settings, Protocols, SSL/TLS, Administrative access, Auto-ban, Backup & restore, Performance, Advanced COM at once, except the syslog card on Logging (since 6.3.4), whose values the sink takes when it starts, at a reinitialise or a service start; the keys as each field's mark says A file on the server (4) REST API keys (hMailServerApiKeys.ini, re-read on every request), Directory authentication (LDAP) and Directory synchronisation ([LDAP], re-read within two seconds), Event scripts (the script file; Save & reload reloads the engine) Without a restart What the table cannot say: Three overview pages are deliberately read-only: Spam filtering overview, Virus scanning overview and Transport encryption overview. They show what no single editor can: a virus scanner that is switched on but cannot run is treated as no virus found, so infected mail is delivered as though examined; a TLS port with no certificate fails to start rather than falling back to plaintext. Discovery (since 6.3.4) changes nothing and is the one page that reads the REST listener, because discovery has no COM interface. It needs RestApiPort set, a certificate it can check for an address beyond loopback, and an administrator without a second factor. A case is opened, searched, held and exported on the Control Deck (eDiscovery and legal holds). Journaling and dual delivery and Audit trail are the server administrator's only. Message trace and Quarantine are inert unless MessageTraceEnabled and QuarantineEnabled are set, and Blocked attachments unless attachment blocking is on in Anti-virus settings. Server limits & expert settings also edits [SendingLimits] and [SendingLimitsOverrides], which stay in the file and are re-read within seconds. Performance also edits the local [Database] rows, and since 6.3.4 has a Message store tab: MessageStoreCompression, off by default, and MessageStoreDedupMinKB, 256. The dialogs. Since 6.3.4 the domain dialog has fourteen tabs (seven in 6.3.3) and the account dialog fifteen (twelve), each ending in Levels. The mark under each field, and the pending bar Since 6.3.4. On 6.3.3 a save of a page whose settings live in hMailServer.INI offers to restart the service. Against a 6.3.4 server a save publishes, and under every field kept in the settings store a small line says when the value takes effect (which keys carry each mark, and what a reinitialise takes The line Meaning Applies on save In effect when the save returns After reinitialize A listener's port or bind address, a TLS context, a work queue's size: waits for Apply now After a restart ServiceAccountName and ServiceAccountPassword, which are retired. The [Database] rows on Performance carry the same words Every key's mark comes in one COM call when such a page is opened, Settings.IniSettingAppliesAll. A page's save writes only the fields whose value changed, because every write publishes, and it says how many of its keys still wait. The pending bar sits at the foot of the window: "N saved setting(s) wait for a reinitialize", with Apply now. The list behind it is Settings.PendingIniSettings, recomputed by the server at every request, so a value put back before anything applied it is not pending. Apply now is Application.Reinitialize over COM: every service stops and starts again in place, with no service restart and no UAC prompt. It asks first. While the server is draining for maintenance the question says that the listeners open again and delivery resumes, because a reinitialise ends the drain. It takes every reinitialize key and not a restart key. When a restart key waits too, what it says afterwards is how many still wait for a service restart, not that the saved settings are in effect. The Control Panel's COM session survives it. The Control Deck's session does not. Three things the pages will not do. Return a key to its default. Clearing a box stores an empty value, which a number reads as 0. The default comes back through DELETE /api/v1/settings/ini/{name}, hmctl ini delete <name> or COM Settings.DeleteIniSetting(name). Change a setting the environment names. A setting set by HM_SETTING_<key> or HM_PROPERTY_<name> (configuration from the environment) is shown disabled, with Set by the environment in place of the mark. Claim more than an older server does. Against a 6.3.3 server, which lacks IniSettingAppliesAll and PendingIniSettings, every field kept in the store is marked After a restart, a save says the service must be restarted, and no pending bar is shown. The Levels tab. Since 6.3.4 the domain and account dialogs list the twenty-seven settings a domain or an account may hold its own value of, ticked where this level holds one, with "In effect: ..., from the ..." beside each. Save levels now writes what changed over COM at once, reads every line back, and shows the server's sentence when it refuses one. Against a server older than schema 6088 the tab says the server has no levels. The help pane Since 6.3.4. Every page has a ? button in the command bar, and F1 does the same: a right-hand pane opens beside the page with what the operator documentation says about it, scrolled to the right heading. It has the document's headings as a list, a search box over the open document, and two links at the foot: Open on the wiki and All documentation. The text is built in. It is the repository's hmailserver/docs/*.md, embedded in the Control Panel as resources (each named Help.<Name>.md, with README.md and DeckParity.md excluded). The Control Panel must work with no REST listener, so it carries its own copy and reads it over no route at all. What is thinner than it sounds. A page whose subject has no document of its own (the TCP/IP ports, the certificates, the groups) opens the nearest one. The search is within the open document only, not across all of them. Open on the wiki is HelpWikiBaseUrl followed by the document's name. The wiki's pages are not named after the documents, so for most documents that link finds no page yet; the pane's own text is the complete one. HelpWikiBaseUrl is a row of hm_settings, shipped as https://gitlab.com/hmailserver/hmailserver/-/wikis/ and edited on the Advanced page under General. A value that is not empty and not an absolute http:// or https:// URL is refused, and empty hides the link. The Control Deck and the webmail have the same pane, served by the server from GET /api/v1/help/{page}. Administering a server on another machine Type the server's name into the Server box and the Control Panel connects over DCOM. That covers everything the COM API can reach. What degrades is anything that means reading a file on the server: On a remote session What you see Live logs "Log folder not found on this machine (live logs need a local server)." REST API keys The key store is a file beside hMailServer.INI; the page needs it The "Listening" badge on TCP/IP ports Every port reads "Unknown": the TCP table probed is this machine's Certificate and private-key inspection "cannot be checked from here" Event scripts The script is loaded from and saved to local disk The [LDAP] and [Database] editors Those sections are read from the file only Editing the [Settings] pages from another machine is not confirmed for 6.3.4. The wiki's Control Panel page says, in section 5.5 on remote administration, that such a page answers "hMailServer.INI was not found on this machine. These settings can only be edited on the server itself." Its 6.3.4 note in section 5.0 says that these keys are now read and written over COM, and that section 5.5 describes 6.3.3 and was not re-checked against the change. Test it on your own installation before relying on it; if a remote page refuses, use Remote Desktop or the REST API. Since 6.3.7 a Control Panel signed in to a server on another computer shows and saves the server's hMailServer.ini [Settings] values, and no longer reads or writes this computer's file for the [Database], [SendingLimits] and [LDAP] values, which it cannot read from there. When the Control Panel itself is the problem Symptom What it means What to do It does not reconnect after you stop the service Deliberate: it rebuilds only a link that broke while the service was up, because activating the COM class would start a service you stopped on purpose Start the service, then use any page An unexpected-error dialog An unhandled UI fault See %LOCALAPPDATA%\hMailServer\ControlPanel\control-panel-errors.log Signing in freezes the window The host accepted TCP 135 and then stalled inside DCOM Check DCOM permissions and the firewall on the server With a server-enforced second factor, a silent reconnect cannot produce a fresh code, so after a service restart you sign in again through the prompt. What it does not do, and where to read more There is no Control Panel on Linux and no COM there; the Control Deck, the REST API and the hmctl client are the surfaces. The Control Panel never returns a key to its default, and it has no page for an account's kind (a person, a meeting room or equipment), encryption at rest, object storage or working an eDiscovery case: those are the Control Deck's and REST's. The public documentation's chapter 20 lists all 65 pages of 6.3.4 by group, and the wiki's Control Panel Pages is the source it follows. Connecting, the shell, the tour and the help pane: https://gitlab.com/hmailserver/hmailserver/-/wikis/The-Control-Panel Every page with its key and its store: https://gitlab.com/hmailserver/hmailserver/-/wikis/Control-Panel-Pages Every page, dialog and field with the setting behind it: https://gitlab.com/hmailserver/hmailserver/-/wikis/Control-Panel-Reference When a saved setting takes effect: https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SettingsApply.md The help pane: https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Help.md The public documentation: https://www.progressiverobot.com/hmailserver-documentation/#5-the-control-panel-your-first-look and https://www.progressiverobot.com/hmailserver-documentation/#20-every-control-panel-page
  • 0 Votes
    1 Posts
    134 Views
    ProgressiverobotP
    This topic is about the settings themselves, whichever interface you use: where a setting is stored, which of yours are not what 6.3.4 ships with, when a saved value is in effect, and how the twenty-seven settings a domain or an account may hold its own value of combine with the server's. The one thing to know first: since 6.3.4 the database is the settings store. A [Settings] line edited by hand in hMailServer.ini changes nothing; at the next start the server uses the stored value, names the key in the error log as HM5804, and, where the service may write the file, puts the line back. Everything below describes 6.3.4 unless it says otherwise; a server on 6.3.3 still reads the file and applies a change at the next start. Two stores, and how a setting is changed The table hm_settings. Everything the COM Settings object has always carried, such as the anti-spam thresholds and the delivery options. Since 6.3.4 every new setting goes here, and no key is added to [Settings]. 6.3.4 added 193 rows for its features. The [Settings] keys. 241 keys read by the settings loader, plus UseLanguage, read per call. On 6.3.3 they live in the file, mirrored into hm_inisettings. Since 6.3.4 (schema 6042) hm_inisettings is the store and the file's section is a copy, which the server keeps current where the service may write the file. Where the service cannot write the file, the copy goes stale. That is a new Windows installation, whose service runs as NT SERVICE\hMailServer, and a Linux package installation, where /etc/hmailserver/hMailServer.ini is 0640 root:hmailserver. There the copy is not refreshed and an edited line is not put back; the database's values are still the ones used. The server reports this once a start in the application log, not as an error, and ServiceAccount.md says the same of the Linux packages; the wiki's Installing on Linux page names HM5806 at a save and HM5803 at every start for them instead. On such a server read a setting from the store, not from the file. The migration is the first start. A key the table has never seen is adopted with the file's value, and there is nothing to run by hand. A line edited after the old server last started, including one edited to prepare the upgrade, is taken for an edit and put back, so make such a change after upgrading. A 6.3.3 server refuses a database at schema 6042 or later with HM5011; see Upgrading to 6.3.4. What stays in the file. [Database], [Directories], [Security] and [GUILanguages] are the bootstrap, read before the database is open and never in the store. [LDAP], [SendingLimits] and [SendingLimitsOverrides] stay in the file. PasswordPepper is kept in the file only, never in the database and never in a backup. [SettingsOverride] is the one way to make the file win, for a database that is unreachable or holds a value the server will not start on: a key written there is applied over whatever is stored and named in the error log as HM5807 at every start, because no page shows it and no backup carries it. Storing a value for a key an override shadows is accepted and reported as HM5808. Surface Set a [Settings] key Return it to the default Control Panel (Windows only) the key's editor, written over COM even on the server itself No. Clearing a box stores an empty value, which a number reads as 0 REST PUT /api/v1/settings/ini/{name} with {"value": "..."} DELETE /api/v1/settings/ini/{name} hmctl, over REST hmctl ini set <name> <value> hmctl ini delete <name> PowerShell module Set-HmIniSetting none COM (Windows only) Settings.SetIniSetting(name, value) Settings.DeleteIniSetting(name) Linux command line, as root hmailserver --set-setting NAME=VALUE, with no listener running. A secret's value is refused on the command line: give the name alone and type the value at the prompt none Control Deck only SmtpTarpitCount, SmtpTarpitDelaySeconds, UseLanguage and RewriteEnvelopeFromWhenForwarding none The REST ini routes take the administrator password only; an API key of any scope is refused. Since 6.3.4 a setting the environment names, as HM_SETTING_<key> or HM_PROPERTY_<name> (configuration from the environment), refuses an administrator's change, 409 over REST, until a start without the variable. What this server has changed from stock Since 6.3.4. One list of every setting whose value is not the one this version ships with, with the shipped value beside it. The example in ChangedFromStock.md: STORE NAME VALUE DEFAULT ini LogLevel 5 9 ini MaxLogLineLen 2000 500 ini SMTPDMaxTimeout 600 1800 ini OAuth2HmacSecret (set) ini DnsServer 10.0.0.1 (forced from [SettingsOverride]) database smtpnoofretries 6 4 database maxsmtpconnections 200 0 7 of 391 settings are not what this version ships with. Where What to ask Control Deck Changed from stock, in the navigation beside Settings REST GET /api/v1/settings/changed Command line hmctl config changed PowerShell Get-HmChangedSetting COM Settings.GetChangedFromDefault The REST route and the COM call answer the same document. store is ini (hm_inisettings) or database (hm_settings). What "default" means. The value the server behaves as though the setting has when nothing has been stored for it, not what a fresh installation's files contain. hMailServer.ini ships with lines the packaging wrote, such as RestApiBindAddress=127.0.0.1, and a line holding the shipped value is not a change; one holding another value is listed. An upgraded installation lists more than a new one. database defaults are what a database created by this version holds. An installation upgraded from an old schema got its values from the upgrade scripts of the time, and where those differ (maxmessagesize, usecache, protocolimap, smtpallowincorrectlineendings and half a dozen more) the older installation is genuinely different and is listed. A row nothing reads any more (useordb, usespamhaus, tarpitcount, filters, imapport, relaymode) is not reported at all. The marks on an entry. secret: a setting whose name says its value is a secret is listed by name, with (set) or empty, and never shown. A key file, such as RestApiPrivateKeyFile, is a path and is shown. known false: the server holds a value and ships no default for it, such as a key stored for a script of your own. hmctl prints (none: this server does not read it). overridden: [SettingsOverride] is forcing the value. Such a key is listed even when neither store holds anything for it; hmctl prints (forced from [SettingsOverride]). environment is variable or file, and hmctl prints (set by the environment). profile names the setting profile whose apply wrote the value, while the setting still holds it. Which values these are. The value stored, not the value the server settled on where the two differ: a PasswordHashIterations outside the range the server accepts is reported as HM5561 and the built-in work factor used; an unusable PreferredHashAlgorithm is reported as HM5528 (or HM5607, for Argon2id on a build whose OpenSSL has none). Who may see it. The administrator password, and nothing else. An API key of any scope is refused with a 401, and an account holding an administrative role, an auditor included, with a 403, because the list includes commands the server runs: AutoBanCommand runs as the service account. The feature changes nothing. It does not compare two servers (two hmctl config changed --json answers and diff do), and it does not say when or by whom a setting was changed; the audit trail does (GET /api/v1/audit?object_type=setting). When a saved setting takes effect Since 6.3.4. Until then every [Settings] key was latched when the service started. Now a save publishes: the store takes the value, the server rebuilds its settings from the rows, and every key whose readers can take a new value is in effect when the save returns, whichever door saved it. Every key carries one of three marks: Mark Keys What it means now 211 A save applies it reinitialize 28 Bound into something the servers build when they start, so it waits for a reinitialise: Apply now in the Control Panel or the Deck, POST /api/v1/server/reinitialize, or Application.Reinitialize over COM restart 31 Read once per process: 2 [Settings] keys and the 29 bootstrap keys (20 [Database], 5 [Directories], 3 [Security], 1 [GUILanguages]) The 28 that wait for a reinitialise. What is rebuilt Keys The REST listener RestApiBindAddress, RestApiPort, RestApiCertificateFile, RestApiPrivateKeyFile The metrics listener MetricsServerBindAddress, MetricsServerPort, MetricsServerCertificateFile, MetricsServerPrivateKeyFile, MetricsServerAuthPassword, MetricsServerAuthToken, MetricsServerAuthUsername The web-services listener WebServicesBindAddress, WebServicesHttpPort, WebServicesHttpsPort, WebServicesCertificateFile, WebServicesPrivateKeyFile The ManageSieve listener ManageSieveServerBindAddress, ManageSieveServerPort The SSL contexts of the SMTP, IMAP, POP3 and ManageSieve listeners and the outbound client TlsCipherSuites13, TlsKeyExchangeGroups, TlsSessionCacheSize, TlsSessionTicketsEnabled, TlsSessionTimeoutSeconds, TlsTicketKeyRotationSeconds ACME, the database pool, the fetch queue AcmeEnabled, AcmeCertificateDirectory, DatabaseStatementTimeout, MaxNumberOfExternalFetchThreads The 2 that wait for a restart are ServiceAccountName and ServiceAccountPassword, which are retired: since 6.3.4 hMailServer.exe /Register takes the account from its command line alone, and a stored value is shown as pending until the next start. The bootstrap keys are never in the store, so none is ever pending. Some keys are now because a subscriber re-latches them. MaxSubmissionsPerIPPerMinute and MaxOutboundPerDestinationPerMinute start each limit's one-minute window again, so a saved limit counts from when it was saved. A cache of data is not a setting: a changed DNSServer is used for the next lookup, and an answer already cached stays until its TTL runs out. Pending. A reinitialize or restart key whose stored value is not the value the server is running on is pending. The list is recomputed at every request and never remembered, so a value put back before anything applied it is not pending. Where What REST GET /api/v1/server/settings/pending answers [{name, stored, running, applies, secret}], and applies and pending come on every /api/v1/settings/ini/{name} answer. The administrator password's route COM Settings.IniSettingApplies(name), Settings.IniSettingAppliesAll, Settings.PendingIniSettings Control Panel Applies on save, After reinitialize or After a restart under each field kept in the store, and a pending bar with Apply now Control Deck The same words as a badge, and Apply now above its Settings view A name the loader does not read (UseLanguage, a key a script of your own reads, a misspelling) has no mark: COM's IniSettingApplies refuses it and REST answers applies: null. Apply now is a reinitialise. Every service stops and starts again in place, which takes every reinitialize key and not a restart key. It asks first. A reinitialise ends maintenance mode, and POST /api/v1/server/reinitialize answers {"reinitializing": true, "drained": true} when it ended a drain. The Deck's session ends with the REST listener it was using; the Control Panel's COM session survives. What a save does not do. It does not re-read the bootstrap sections or [SettingsOverride]; a reinitialise or a start re-reads the override section and announces it as HM5807. It does not reconcile the file against the table; that is a start's and a reinitialise's only. And a value stored by another process (another node on the same database, or hmailserver --set-setting while the service runs) reaches this server at the next save here, a reinitialise or a restart, because nothing watches the table. hmctl has no pending verb. On Linux the regression fixture alone proves the marks, with the three COM members answered over REST; the internal tester that swaps settings under load does not run there. One value, three levels Since 6.3.4 (schema 6088). Twenty-seven settings may be set for the server, for a domain and for an account, and every place that shows one shows what is in effect and where it came from. For an account: its own value, else its domain's, else the server's. Every setting on the list is exactly one kind: Kind How many The rule Who sets a level below the server Limit 15 A level below may tighten the level above and never loosen it. A looser value is refused when it is written; a value the level above has since tightened past is capped when it is read Anybody who administers that level: a domain administrator, a key restricted to the domain Gate 4 The deepest level that holds a value wins, either way The server administrator alone; a domain-restricted key is refused with 403 Preference 8 The deepest level that holds a value wins The domain's administrators, and a domain-restricted key The last column is the rule for the levels held in hm_settingoverrides. A field the domain already had keeps the authority it had, whatever its kind: the domain's maximum message size and the domain's retention are the server administrator's. Kind Settings Limits max_message_size_kb, session_idle_seconds, session_lifetime_seconds, session_one_per_device, session_bind_to_address, quota_grace_percent, quota_send_stop_percent, recall_days, recover_deleted_days, sending_max_messages, sending_max_recipients, reader_fetch_accounts_max, reader_sender_addresses_max, reader_masked_addresses_max, reader_offline_days_max Gates plus_addressing_enabled, openpgp_portal_enabled, zero_access_allowed, directory_user_editable Preferences quarantine_digest, quarantine_digest_hour, external_tag_subject, external_tag_header, impersonation_action, quota_warning_percent, country_policy, message_retention_days A server setting not on the list is the server's alone: its name is refused on the domain and account routes (400, unknown field) and by COM's SettingOverride. Of the [Settings] keys only QuotaWarningPercent has levels. Where each level is held. A field the domain or the account already had, such as the domain's MaxMessageSize or MessageRetentionDays, stays where it was, with the meaning it always had; its 0 or -1 is the level holding nothing. The rest are rows of hm_settingoverrides, where no row means "take the level above". An owner's rows are cached for sixty seconds; every write through the interfaces drops the cache at once, and so does POST /api/v1/settings/cache/clear or COM Cache.Clear. A row put in by hand that the interfaces would refuse is treated as holding nothing and reported as HM7311. Nothing changes for a domain or an account that holds no value of its own. Where What REST GET /api/v1/settings/levels lists the twenty-seven. GET /api/v1/domains/{domain} and GET /api/v1/accounts/{address} answer <name>_effective and <name>_source, and <name> where the level is held in the overlay. For a level held in the overlay a PUT takes a value of the setting's own JSON type, and null makes it inherit; for a level held in a field of its own null leaves it unchanged, and the field's own 0 or -1 is "take the level above" COM (Windows only) Domain.SettingOverride(name) and Account.SettingOverride(name), an empty string for "take the level above", written at once and not by Save; EffectiveSetting(name) and EffectiveSettingSource(name) hmctl hmctl domain set <domain> <name> <value\|inherit> and hmctl account set <address> <name> <value\|inherit> Control Panel (Windows only) A Levels tab on the domain and account dialogs, with "In effect: ..., from the ..." beside each setting and Save levels now Control Deck The same card in its domain and account editors A saved level applies at once, at every level and from every door, because the resolver is asked when the value is used (at MAIL FROM, when a session is checked) and never latched. Deleting, backing up and moving. Deleting an account or a domain deletes its rows; a delete that cannot remove them is reported as HM7310 for a domain and HM6109 for an account. The XML backup carries the rows, and on restore a row the build refuses is left out and reported as HM7312. A mailbox moved to another domain keeps its own values. What is thinner than it sounds. Attachment blocking, web push, passkeys and account recovery have no levels. The domain's session seconds and switches and its quota grace and send-stop shares could always be set looser than the server's and still can; the ceiling binds the account below them. The maximum message size is resolved at MAIL FROM and at IMAP APPEND; the EHLO SIZE advertisement, the webmail's own send and the send API stay the server's. The session policies are the browser sessions' (the webmail, the portal, the Deck); IMAP, POP3 and SMTP sessions have none. country_policy cannot be set to empty over COM, where an empty string means "take the level above"; REST's "" does it. Twelve of the twenty-seven are proved by what the account and its domain answer rather than by driving the behaviour per account, and the levels' own regression fixture is Windows-only. When a setting appears to do nothing Is it pending? A reinitialize key waits for Apply now and a restart key for a service restart. Was it typed into hMailServer.ini? The stored value wins and HM5804 names the key. Store it instead. Is the key spelled exactly as the code reads it, and is a boolean literally 1? An unknown key is the default, and only 1 is true: 2, yes and true all read as false. Does it need a companion? SRSEnabled needs SRSSecret, ACME needs AcmeDomains, autoconfig needs WebServicesHttpPort or WebServicesHttpsPort, and MTA-STS hosting needs the HTTPS one. Does the error log show HM5807 or HM5808? A [SettingsOverride] section is forcing a value. Where to read more The tour: https://gitlab.com/hmailserver/hmailserver/-/wikis/Settings-Overview. Every key with its default, validation and mark: https://gitlab.com/hmailserver/hmailserver/-/wikis/Settings-Reference. Where the two disagree, the reference is generated from the code and is right. The three documents behind this topic: https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/ChangedFromStock.md, https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SettingsApply.md and https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SettingLevels.md The public documentation's settings chapter: https://www.progressiverobot.com/hmailserver-documentation/#21-settings-reference-hmailserverini On this forum: the Control Panel on Windows, hmctl and the PowerShell module, the hmailserver command line, the Control Deck, Upgrading to 6.3.4 and hMailServer on Linux.
  • 0 Votes
    1 Posts
    135 Views
    ProgressiverobotP
    This topic covers the built-in backup, what its archive holds and leaves out, and restoring a server, one mailbox or one message. Know this first: a restore is destructive and has no transaction. It deletes every domain, every account and every public folder before it puts the archive's back, so whatever an account has that is not in the archive is deleted and not replaced. And the archive does not include your messages unless you tick the box. What one run writes One 7-Zip archive per run, named HMBackup YYYY-MM-DD HHMMSS.7z, in the configured destination: an XML index and, with Back up messages on, the whole data directory beside it. The data directory is C:\Program Files\hMailServer\Data by default on Windows and /var/lib/hmailserver on Linux, and what lives inside it travels with the messages: Sieve scripts and their state (<data directory>\Sieve), and ACME certificates and keys (<data directory>\ACME, unless AcmeCertificateDirectory moved them). Since 6.3.4, messages held in an object store (MessageStoreBackend) are read into the archive too: a store that cannot be read fails the backup, a restore puts the files back on the local disk, and the store's credentials are not carried. What it cannot hold: files the database only points at. Manually placed TLS certificates, private keys and DKIM keys are stored as paths. Restored onto a machine where those paths hold nothing, the server starts, serves no TLS and signs nothing. Keep them inside the data directory, or copy them yourself. What is in the archive Area Carried Domains Aliases, distribution lists and members, signatures, DKIM selectors and key paths; since 6.3.4 the domain's allow and block lists, its spam traps, the domain transforms, the S/MIME authority and the recovery key of a domain encrypted at rest Accounts Address, password hash and its algorithm, forwarding, vacation, signature, quota, admin level, anti-spam thresholds, TOTP secret Per account, every release Rules, fetch accounts with their passwords, application passwords as hashes and, with Back up messages, folders and messages with UID and flags Per account, since 6.3.4 Message keywords (the webmail's labels, second and third stars, follow-up flags, mute and pin), the address book, webmail preferences, scheduled sends and snoozes, file-link records, S/MIME keys, the password history, calendars with their events and tasks, the first-contact memory, safe and blocked senders, the mailbox key of an account encrypted at rest, the role assignment Public folders With the permissions on them. Backup.md states no condition; section 15.2 of the wiki lists them under Back up settings. A restore deletes every public folder first Settings, with Back up settings The hm_settings property set and the [Settings] rows of hm_inisettings; routes, IP ranges, global rules, TCP/IP ports, SSL certificate records, incoming relays, groups, blocked attachments, the DNSBL, SURBL and white lists; since 6.3.4 the remote domain policies and IP warm-up schedules Custom administrator roles With domains or settings; restored first, so the assignments accounts carry find their role Nothing identity-shaped is written. A restore gives every row a new id, which makes the archive portable between database backends; a reference that cannot be resolved drops that one row and names the account in hmailserver_backup.log. Secrets travel opened. Route, fetch-account and SSL private-key passwords are carried in a portable encoding and protected again on the restoring machine (since 6.3.8 one the encoding cannot carry, holding a character above U+00FF or longer than 248 characters, as an external account's OAuth token often is, is left out of the archive and named in the error log and the backup log as HM7280, to be entered again after a restore, where 6.3.7 and earlier wrote it empty); since 6.3.4 each TOTP secret and the [Settings] secrets (SRSSecret, OAuth2HmacSecret and the rest) are carried opened and sealed again under the restoring server's stored-secrets key, so a restore onto new hardware keeps every second factor. An unencrypted archive therefore hands those secrets to whoever can read the destination folder. In 6.3.3 and earlier the per-account rows marked since 6.3.4 are in no archive, and a restore deletes them. Until you upgrade, back up the database itself with its own tool (pg_dump, mysqldump, SQL Server's backup, or a copy of hMailServer.sdf with the service stopped), restore from that when recovering a whole server, and tell users before they find their address book and labels gone. An archive written by 6.3.3 or earlier restores on a newer server as it always did. What is not in the archive, deliberately Not carried Consequence of a restore [Database] and [Directories] in hMailServer.INI; the administrator password and its second factor Per machine. Keep a copy of the file, and never copy one machine's file over another's PasswordPepper (since 6.3.4) Kept in the file only. A restore onto new hardware needs the same pepper in that machine's file before an Argon2id or scrypt password can sign in; PBKDF2, SHA-256 and MD5 hashes, and the administrator password, do not use it The stored-secrets key (since 6.3.4) The archive does not need it. A database-level backup does: restored without the exported key, every second factor and [Settings] secret in it is unopenable. Export it once (hMailServer.exe --export-stored-secrets-key FILE, elevated, or sudo hmailserver --export-stored-secrets-key FILE) and keep it with the database backups, stored apart from them On Linux, the stored-secret key file <DataFolder>/.hmailserver-secret-key The archive does not carry it. Its passwords travel in a portable encoding and do not need it, but a domain's S/MIME authority key, and the mailbox key of an account encrypted at rest that has not yet signed in, are carried as envelopes that open only where this file is: restored without it, the authority must be rotated before it signs. A database-level backup needs it as well: back it up with the database, because a secret moved to a machine without it cannot be opened (HM6412) The delivery queue Queued files are copied with the data directory, but nothing re-queues them. Let the queue drain before a migration Per-domain relay settings, and per-domain message retention days Silently lost, and silently reset to 0 (keep for ever). Re-enter them Greylisting triplets; auto-ban and login-failure state Senders are greylisted afresh; bans lapse. Permanent IP ranges are carried Quarantine, message trace, metric history; since 6.3.4 the audit trail, alert rules and events Operational records. Back the database up with its own tool if you need them Deleted items kept for recovery, eDiscovery cases and holds, messages that expire, password-protected messages The Recoverable, Discovery, Confidential and Protected folders are left out of the copy and left in place by a restore. A restore with domains is refused while an account or a domain is under a legal hold. Archives taken by earlier 6.3.4 builds, before Confidential and Protected were left out, do hold both: a message that has expired or been withdrawn since is still in them, so apply the mail's own retention to those archives, or delete them The full-text index, IMAP annotations, the QRESYNC expunge log Rebuilt, or keyed on ids a restore reassigns. IMAP clients resynchronise Passkeys; calendar and address-book shares; the backup passphrase, the SNMP passwords, the object store's secret, the sandbox token Enrol, share or enter them again A domain named recoverable, discovery, confidential or protected keeps its mail in a folder the backup leaves out. Such names are refused for a new domain since 6.3.4, and an existing one is reported as HM7597; rename it before relying on the backup. The options, and the combinations that fail Maintenance → Backup & restore in the Control Panel (Windows-only), the Control Deck's Backup view, PUT /api/v1/settings/backup and hmctl settings set backup set the destination and what goes in. A fresh install seeds backupoptions as 0: nothing is ticked. On a packaged Linux server the destination has to be inside the unit's ReadWritePaths, or the write fails; add it with a drop-in. Option What it changes Back up domains The domain tree goes into the index. Required for messages to mean anything Back up messages Copies the whole data directory. Only with Back up domains as well: messages without domains stages nothing and then fails Message metadata only (BackupMessagesDBOnly=1) Records which messages exist, not their contents; a restore expects the files still to be in the data directory. It governs the restore as well as the backup, and must be the same for both Back up settings The settings row above and, per the wiki, public folders Compress destination files Puts the message files inside the archive. Off, every run overwrites one DataBackup folder beside the archives: exactly one generation of mail, whatever retention says Verify each backup (BackupVerifyRestore=1, the default) The next section Tick domains and messages together, always, and Back up settings as well if the installation has public folders. The index is written to hMailServerBackup.xml, added to the archive and deleted, so what you restore from is the .7z; the Control Panel's file picker defaults to *.xml, so choose All files. How a run is verified Every refusal happens before anything in the destination is written, or deletes only the archive this run created. A run refuses when a message file lies outside the data directory, when the destination is unreachable, and when the database is down or any read did not run, so an outage cannot produce a hollow archive. Then the archive is read back: The index opens, through the same code a restore uses, and describes this run. If not, the backup fails and the archive is deleted. The per-account rows are all there (since 6.3.4), counted per table. A difference fails the backup. The message store extracts to exactly the files and bytes that were staged. Fewer is a truncated archive, whatever 7-Zip's exit code said. Every message row has a file. Reported, never failed: mail flow does not stop for a backup. The log names the first ten. Steps 3 and 4 run only when domains and messages with their files are included and BackupVerifyRestore is 1. The wiki puts step 2 under the same conditions; Backup.md says only that BackupVerifyRestore=0 turns off steps 3 and 4. Step 3 costs disk: the store exists twice in the temp directory during the check, and a temp volume that cannot hold it skips the step with an explanation rather than failing a good backup. A successful run ends Backup completed successfully.; a failed one has a BACKUP ERROR line. Schedule and retention Since 6.2.25. Nothing is configured on a fresh install. Setting Default Meaning ScheduledBackupTime empty (off) HH:MM, local time; at most one backup per calendar day ScheduledBackupIntervalMinutes 0 (off) Used only when no daily time is set; measured against the newest archive in the destination ScheduledBackupKeepCount 0 (keep all) How many of this server's own archives to keep ScheduledBackupMaxAgeDays 0 (keep all) Delete archives older than this UpdateBackupBeforeApply 1 An unattended live-update apply (in UpdateWindow) backs up to the destination first and is cancelled without a successful backup; an apply you start yourself takes none Since 6.3.4 these are stored in the database: set them on the Backup & restore page, with PUT /api/v1/settings/ini/{name} or hmctl ini set, not in the file (how the settings store works). Retention has four rules: nothing is deleted until the replacement has completed successfully; the newest archive is never deleted; the age rule always leaves the two newest alone; and only files whose names the server generated are considered, the timestamp read from the name. A scheduled run certain to fail (no destination, nothing selected, database unavailable, destination unreachable) is refused before it starts and backs off 5, 10, 20, 40 and then 60 minutes. Test backup directory on the Control Panel's Diagnostics page catches a schedule that has been failing silently. Encrypted archives, since 6.3.4 Off until a passphrase is set; then every archive is 7-Zip AES-256 with its headers encrypted (-mhe=on), so it cannot even be listed without the passphrase. Set it in the Control Panel (Backup & restore → Encryption), the Deck's Backup view, encryption_passphrase in PUT /api/v1/settings/backup, or hmctl backup passphrase set. It is 12 to 256 printable ASCII characters, kept in the database under the machine's secret store (DPAPI on Windows, the key file on Linux), never written into the archive, a command line or a log, and never read back. If it is lost, everything in every archive written with it is lost. Changing it does not touch archives already written, so write the passphrase and its date down together, away from the backups. What it changes about a run. Compression must be on when messages are backed up, or the run is refused. Staging moves into a directory under TempDirectory, so the temp volume needs room for the message store plus a little, and the run is refused with both numbers if it is not there. A stored passphrase this machine cannot open fails the backup; an unencrypted copy is never written in its place. 7zr, the reduced p7zip build, cannot encrypt: a Linux server needs 7-Zip (7zz) or p7zip-full. The archive's file name, its size and the fact that a backup ran are not hidden. Restoring the whole server Phase one decides and changes nothing. Since 6.3.4 the archive is listed first (7za l -slt, the headers only): an entry with an absolute path, a .. segment, or a link with somewhere to point refuses the whole archive as HM7540, because no backup the server writes has such an entry. Then the archive must be one of ours, written by an older or equal version (a newer one is refused, with no override), and hold what you asked to restore; then the store is extracted to a scratch directory. Phase two acts: delete every domain, delete every public folder, empty the data directory and copy the staged store in, load domains and accounts, load settings, reinitialise. A failure here leaves a half-restored server. The listing check works whichever 7-Zip is installed; the Windows installer's is 26.03 in 6.3.4, replacing 19.00. Where. The Control Panel's Restore card and COM (BackupManager.LoadBackup, LoadEncryptedBackup) on Windows; POST /api/v1/backup/restore with file, passphrase and the three parts, which is the only restore a Linux server has; the Deck's Backup view; hmctl backup restore FILE --domains --messages --passphrase-file FILE. The REST route's check is bounded at two minutes; past that the route answers 504 and starts nothing. Install the same version, at the same drive letter and directory layout. Restore or recreate [Database] in hMailServer.INI and confirm you can connect. Choose the .7z, tick the categories, start, and watch hmailserver_backup.log. Copy back what the archive could not hold: certificate, key and DKIM files, and PasswordPepper. Re-enter the per-domain relay settings. Check Server status, then Diagnostics. Send a message in from outside, read it over IMAP, send one out. Rehearse this yearly on a scratch machine against a new, empty database, never production, and keep the list of what you fixed by hand with the backup. Restoring one mailbox, or one message, since 6.3.4 An archive in the backup destination can give back one account's mailbox, every folder and message it held when the backup was taken, or one message of it, and touch nothing else: not the account's settings, rules, contacts or calendars, and no other account. Where How REST GET /api/v1/backup/archives, GET /api/v1/backup/{archive}/mailboxes/{address}, POST .../restore with mode, or message and folder, GET /api/v1/backup/restore-mailbox/{job} Control Deck, and Control Panel (Windows) The Restore a mailbox and Restore one message cards COM (Windows) BackupManager.RestoreMailbox, RestoreMessage, DescribeMailbox, MailboxRestoreStatus hmctl hmctl backup archives, hmctl backup mailbox ARCHIVE ADDRESS, hmctl backup restore-mailbox ARCHIVE ADDRESS --merge\|--replace [--message ID [--folder F]], hmctl backup restore-status JOB Which archives. Only one inside the destination, named as the listing names it and never by an arbitrary path. It must have been taken with domains and messages; an archive taken with BackupMessagesDBOnly holds rows and no files, and the job fails. An encrypted one needs its passphrase. Merge creates each archived folder that is missing and adds each archived message whose file is not in the live mailbox; nothing is removed, and merging twice adds nothing. Replace empties every live folder first, so the mailbox is the archive's and mail that arrived since the backup is gone; what it removes is kept only for RecoverDeletedDays, which is off by default. Replace is refused with 400 when the archive holds no messages for the account. The identity rule. Every message file is named once, with a GUID, and the same name anywhere in the live mailbox is the same message: a message the person moved is not brought back to its old folder, and one they deleted is. A restored message keeps its flags, keywords and dates, gets a fresh UID, and is charged to the account as a delivery is. Refused, and counted. The account must exist on this server and the archive must hold it (404); restoring into a different account is not offered, and neither is a public folder. A second restore while a backup or restore runs is 409. The outcome (folders, added, skipped, failed) is in the job's status and the backup log. Restoring needs manage_server; listing an account's messages out of an archive needs view_mailbox. Where to read more Wiki: Backup and Restore, with COM commands in section 15.9; Installing on Linux for the Linux paths and key file. Repository: Backup.md; SecretsAtRest.md. The public documentation: Backup and restore, Backups on Linux. On this forum: Moving to new hardware, Upgrading to 6.3.4, and Hardening a public mail server for exporting the stored-secrets key.
  • 0 Votes
    1 Posts
    143 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) fixes the SMTP log writing some credentials in the clear with SMTP logging on: an AUTH PLAIN sent before EHLO or not in its one exact form, the bearer token of AUTH XOAUTH2 and AUTH OAUTHBEARER, and the lines of SCRAM-SHA-256, OAUTHBEARER and GSSAPI went into the log as the client sent them, base64 and readable, in every version from 6.0.0 to 6.3.6. An SMTP log written by 6.3.6 or earlier may hold them: redact every line of a SASL exchange before posting one. A database write that failed is now reported (HM5032) where it was taken as done, so an error log that was quiet may name failures that were hidden; and the IMAP and POP3 listeners can take the client's address from a PROXY protocol header, as the SMTP listener does, both off by default. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) no longer reports a stop of the servers as errors of their own: work that outlasted a stop, a reinitialise among them, writes a line to the debug log where it wrote HM5136 or another code to the error log and ran an installed OnError script, so the error log of a server that reinitialises may be quieter; and an event script's call that meets a stop fails with "The server is stopping. The request was not completed.", its script error going to the debug log. A failed account sign-in at the REST listener from loopback, as through a reverse proxy on the same machine, is counted against the name alone and writes no Auto-ban: logon failure line for fail2ban to read. See the 6.3.8 announcement. This topic covers what hMailServer writes to disk and which file to open, the settings that decide how much is written and kept, the live views, following one connection (since 6.3.4), a copy to syslog or the journal, and the fail2ban filter the Linux package installs. The one thing to know first: open ERROR_hmailserver_<date>.log before anything else. It holds only the entries the server itself classed as an error, and its mere existence is a diagnostic, because the Control Panel's built-in self-test (Monitoring & troubleshooting → Diagnostics, so Windows) fails when any ERROR* file is present in the log directory. Where the logs are Platform Log directory Set by Windows C:\Program Files\hMailServer\Logs by default [Directories] LogFolder in hMailServer.INI Linux /var/log/hmailserver, created by the package as 0750 hmailserver:hmailserver and left in place when the package is removed [Directories] in /etc/hmailserver/hMailServer.ini The format is the same on both platforms. On Linux the journal (journalctl -u hmailserver) carries the startup line, anything written to standard error and the operational events; a memory-safety fault is recorded in /var/log/hmailserver/crash-oracle.log as one line (signal, address, thread, event number) before the process dies by the signal it caught. [Directories] is per machine and is never mirrored into the database, so do not copy another server's file over this one's. The files File Holds Rotates hmailserver_<date>.log Everything: application lines, and the protocol conversations unless they are split out Daily, by name ERROR_hmailserver_<date>.log Only entries the server classed as an error Daily, by name hmailserver_SMTP_<date>.log, hmailserver_IMAP_<date>.log, hmailserver_POP3_<date>.log The per-protocol conversations, when SepSvcLogs=1 Daily, by name hmailserver_backup.log Every backup and restore run, line by line Never: one growing file. LogDeleteDays matches it by name but deletes only a file nothing has written to for that many days hmailserver_events.log Event-script output Never, as above hmailserver_awstats.log The AWStats-format feed, when AWStats logging is on Never, as above hMailServer_messagestore_consistency.report The most recent message-store consistency scan: one line per message whose file is missing, as messageid<TAB>account<TAB>expected-path Overwritten each run The backup and event logs are among the files the server marks with a byte-order mark; they are UTF-16LE on both platforms, so a file written on one reads on the other. A log line looks like "IMAPD" 5220 41 "2026-09-17 09:00:00.120" "192.0.2.7" "SENT: A1 OK LOGIN completed", where IMAPD is the category and 41 is the session id that every line of that conversation carries. The conversation categories are SMTPD, SMTPC, POP3D, POP3C and IMAPD; APPLICATION, TCPIP, DEBUG and ERROR are the rest. The settings Setting Default What it does What it looks like when it is wrong LogLevel 9 Not the choice of log types, which is the Logging page's tick-boxes (or PUT /api/v1/settings/logging). The Settings Reference records two effects: long lines are cut only when it is 2 or lower with debug off, and IMAP SENT: lines for FETCH and STATUS are logged only when it is above 2 or debug is on — Debug messages (the Logging page) off Adds the LOG_DEBUG lines, including the accept-pipeline stage timings Left on, a busy server fills the disk in days, and a full disk stops mail SepSvcLogs 0 Splits SMTP, IMAP and POP3 into their own files Off, so a search for one protocol wades through all three LogDeleteDays 0 Deletes files named hmailserver_*.log or ERROR_hmailserver_*.log whose last write is older than this many days, and nothing else in the directory. 0 keeps everything for ever Left at 0 on a server with debug logging on: the usual cause of a full log volume JsonLogging 0 Writes JSON lines, for Elasticsearch, Loki or Splunk Overridden while Logging.LogFormat is NCSA: the pipeline gets NCSA lines and no error SlowQueryLogMilliseconds 0 (off) Logs, and counts in hmailserver_db_slow_queries_total, any database statement slower than this Off, so a database that has become slow leaves no trace but the latency histogram WindowsEventLogLevel 2 How much also goes to the Windows event log — Where they are set. On Windows, Monitoring & troubleshooting → Logging in the Control Panel. On either platform the table's [Settings] keys (all but Debug messages) are set with PUT /api/v1/settings/ini/{name} or hmctl ini set. On Linux there is no Control Panel, so the log categories, debug messages among them, are set with PUT /api/v1/settings/logging, hmctl settings set logging or the Deck's Settings page. There, WindowsEventLogEnabled=1 is a Windows inheritance in name only, because on Linux it means syslog, and the operational events reach the journal through it. Since 6.3.4 these keys are stored in the database, whose value is the one used: where the service can write hMailServer.INI, a line edited there is put back and reported as HM5804. How the settings store works has the rest, [SettingsOverride] (HM5807) among it. Rotation. On Windows, LogDeleteDays is the rotation. On Linux the package installs /etc/logrotate.d/hmailserver; the rule uses copytruncate because the server does not reopen its log on a signal, and the rule says so at length. Debug logging: on to diagnose, off afterwards. The stage-timing lines that diagnosing stalled mail depends on are debug lines; the slow ones (ten seconds or more) are written at application level as well. Leaving debug on matters because a full disk stops mail: the application log warns below DiskSpaceWarningThresholdMB (1024 MB), and below MinimumFreeDiskSpaceMB (100 MB) on the message store's volume the server answers 452 4.3.1 Insufficient system storage. and senders retry. The live views Windows: the Control Panel. Monitoring & troubleshooting → Live logs is a real-time, colour-coded tail of what the server is writing; every page in that group but Discovery reads the running service over COM (Discovery has no COM interface, so it reads the REST listener and needs RestApiPort set). Diagnosing stalled mail, in the same group, has a one-click debug-logging toggle. Both platforms: the Control Deck and hmctl. The browser Control Deck at / on the REST listener has a Logs page that lists the log files and shows any file's last 200 or 2,000 lines, without a share to the log directory. Since 6.3.4, hmctl log list and hmctl log tail <name> --lines N read the same over GET /api/v1/logs and GET /api/v1/logs/{name}, and work against a Windows or a Linux server because they go over HTTP rather than DCOM. The Linux packages install hmctl as /usr/bin/hmctl; the HmailServer PowerShell module is the same vocabulary for PowerShell. The files themselves, from a shell. On Windows: $log = "C:\Program Files\hMailServer\Logs\hmailserver_" + (Get-Date -Format "yyyy-MM-dd") + ".log" Get-Content $log -Tail 0 -Wait | Select-String "SMTPD|SMTPConnection" On Linux: tail -f /var/log/hmailserver/hmailserver_$(date +%F).log journalctl -u hmailserver -f Following one connection, since 6.3.4 Every SMTP, POP3 and IMAP conversation is a session with a number, and every line the log writes for it carries that number. Until 6.3.4 that meant searching the day's log for the number afterwards. Since 6.3.4 the Connections section at the top of the Deck's Logs page lists every connection the server has open now, newest first, and the last two hundred that ended: the id, the protocol, the address it came from, the port it came in on, the account once it has signed in, and when it started or ended. Follow beside any of them opens a pane holding every line the log has for that session and keeps adding lines as the server writes them; Copy puts the whole pane on the clipboard; Stop following ends it. A session that ended a second ago is still listed, so an SMTP conversation that lasted a quarter of a second can be picked and read. How it works, and what that means for you. The server keeps the last 4,096 log entries in memory, across every session; the ring is always on, and nothing survives a restart. A debug-logged IMAP client can fill it in minutes, so a follow whose cursor points before the oldest entry still held answers dropped: true and starts from what is left; the page says so. A line older than that is in the log files and nowhere else. The conversation lines (SMTPD, SMTPC, POP3D, POP3C, IMAPD) carry the session id in their own column. DEBUG and TCPIP lines carry none and are filed by the first number after the word session in their text (TCP connection started for session 41, Ending session 41); that is an inference, and the text is not always the server's own. A line that does neither is nobody's and is not shown. It shows what the log mask lets through, and no more. With IMAP logging off, an IMAP session is listed and its follow is empty; the answer's logged field says whether the log mask lets that protocol's lines through, and the page names the switch to turn on. Debug logging is what makes the TLS handshake, the timeouts and the disconnect appear beside the conversation. The connections the server opens itself are listed too, marked client: SMTP client, POP3 client, IMAP client, and the short-lived SpamAssassin and Diagnostics connections. Not listed: anything that is not a TCPConnection, so the REST API, the webmail, the DAV services, ManageSieve and the anti-virus scanner's socket to clamd are not sessions here. A delivery to a host that refuses the connection is not listed either, though with debug logging on its lines can still be followed by id, with known: false. A follow is a long poll, not a stream: GET /api/v1/logs/sessions/{id}?after=0&wait=10 answers the session's lines after sequence 0 and a cursor to ask after next time, waiting up to wait seconds for the next line. The wait is at most twenty seconds, and one waiting follow at a time is allowed, because a waiting follow holds one of the HTTP listener's four worker threads, which also serve the webmail; a second follow is answered at once with waited: false. An answer carries at most 500 lines, and the pane keeps the last 5,000. GET /api/v1/logs/sessions lists the sessions (the newest thousand when more are open, with truncated: true), with held_lines and logging_enabled. Both routes are under the view_logs permission, which the built-in read-only auditor role holds, and both are server-wide: a credential confined to some domains is refused them, as it is refused the log files. There is no Control Panel page and no hmctl verb for this; the Control Panel's Live logs is the older, whole-log form over COM. There is no filtering inside a session. A copy to syslog, or to the journal, since 6.3.4 Since 6.3.4 every entry the server logs can also be sent as RFC 5424 to a syslog collector or, on Linux, where the server runs as the packaged systemd unit with no collector host named, to the journal. It is a copy: the files above are written exactly as before, and nothing it does can stop a line reaching them, or delay or lose a message. Its settings are hm_settings rows, not [Settings] keys, read when the sink starts (the next service start or reinitialise), not when they are saved. With the journal on, journalctl -u hmailserver HMAILSERVER_SESSION=41 follows one session from the journal, across a restart. Alerts, the audit trail, syslog and the journal has the twelve settings, the transports, the rsyslog configurations and the journal's fields. Codes in the error log worth knowing since 6.3.4 Code What it says, and what to do HM5804 A value typed into hMailServer.INI [Settings] for a key the database holds was put back at the last start. Change it in the Control Panel, with PUT /api/v1/settings/ini/{name}, hmctl ini set or COM Settings.SetIniSetting HM7460 AutoBanFirewall is on and the Windows service account cannot write firewall rules (below) HM7495 The database records a stored-secrets key this server does not hold, and every account's second factor refuses every code: import the exported key with hMailServer.exe --import-stored-secrets-key FILE (elevated) or sudo hmailserver --import-stored-secrets-key FILE, then restart HM7540 A restore was refused because the archive holds an entry with an absolute path, a .. segment or a link with somewhere to point; nothing was extracted. Find out where the archive came from fail2ban on Linux, and the Windows contrast An auto-ban (repeated failed logons from one address) is enforced by the server's own listeners: the kernel accepts the connection and the server refuses it. Shipped in 6.3.2, the Linux package offers two ways to push the ban below the server, and one is enough. fail2ban needs no capability at all, because it runs as root and reads the application log. The package installs /etc/fail2ban/filter.d/hmailserver.conf and a jail at /etc/fail2ban/jail.d/hmailserver.conf, disabled as Debian asks of a shipped jail. The filter matches the two fixed-shape lines the server writes for every counted failure and every ban, whether or not protocol logging is on; auto-ban on logon failure (AutoBanOnLogonFailureEnabled, on as shipped; auto_ban_on_logon_failure over REST) is what writes them. Turn it on: printf '[hmailserver]\nenabled = true\n' > /etc/fail2ban/jail.d/hmailserver.local systemctl reload fail2ban fail2ban-client status hmailserver The server itself, with AutoBanFirewall=1, makes the firewall match its auto-ban ranges at start, on every ban, on every range deletion and once a minute. On Linux it runs /usr/lib/hmailserver/autoban-hook, which keeps an nftables set with a timeout (iptables where there is no nft) and drops TCP from the banned address to the ports the server listens on. Changing the firewall needs CAP_NET_ADMIN, which the unit deliberately does not grant a mail server by default. Grant it once with a drop-in, and the hook's check verb says whether it can act: systemctl edit hmailserver [Service] AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN systemctl restart hmailserver sudo -u hmailserver /usr/lib/hmailserver/autoban-hook check AutoBanCommand, on either platform, runs a program of yours instead of, or as well as, AutoBanFirewall (<command> ban <address> <minutes> <ports> and <command> unban <address>) for a cloud firewall or a router. Before turning either on, list the addresses you administer from in AutoBanNeverBan: a ban that reaches the firewall locks that address out of every mail port on the machine. Since 6.3.4 AutoBanFirewall, AutoBanCommand and AutoBanNeverBan are stored settings like the other [Settings] keys: set them over the API or with hmctl ini set, not in the file. The fail2ban filter and the hook are part of the Linux packages. On Windows, AutoBanFirewall writes one inbound block rule per banned address in Windows Defender Firewall, named hMailServer auto-ban <address> and scoped to the ports the server listens on, and since 6.3.4 a new installation's service account, NT SERVICE\hMailServer, cannot write them: HM7460 is logged once a start and no rules appear, while the listeners still refuse a banned address. Turn AutoBanFirewall off there, or use AutoBanCommand towards a router or cloud firewall; HM7461 names rules a LocalSystem run left behind, with the PowerShell that removes them. What to include in a report SUPPORT.md asks for, in order of value: the relevant log, not a description of it, with debug logging on and the window around the problem; the ERROR_hmailserver_<date>.log for the same window; the exact version, the platform and the database backend; and what you expected instead, and whether it is reproducible. Remove passwords, keys and real email addresses first, because a posted log is public. Read this before you post asks for the same log lines and adds the build number, the package, whether the server was upgraded and the Diagnostics output; Copy on the Diagnostics page puts the whole result on the clipboard. Without a GitLab account, email the report to the project's Service Desk address, contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, which GitLab files as a confidential issue; the maintainers answer by email. Where to read more Wiki: Monitoring and Health, sections 16.2 and 16.2a; Troubleshooting, sections 19.10 and 19.11; Installing on Linux, for the journal and fail2ban. Repository: FollowConnection.md, Syslog.md, SUPPORT.md. The public documentation: Logs on disk, Logging and log rotation on Linux, Follow one message end to end in the logs, Codes in the error log. On this forum: hMailServer on Linux: what you get and what you do not, The Control Deck, Diagnosing stalled mail, and IP ranges, auto-ban and lockout for when a ban is made and how to lift one.