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
    163 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, and a monitor that matches "The response carried no HTTP headers." needs the new sentence, which says who ended the connection, after how many bytes and seconds. See the 6.3.6 announcement. hMailServer 6.3.7 (17 October 2026) publishes registry.gitlab.com/hmailserver/hmailserver:6.3.7, tagged 6.3 and latest until 6.3.8; renames the Control Panel page that holds the metrics listener's settings from API & monitoring to Monitoring & alerts, with the alerts' settings, the outbound webhooks and the SNMP agent on it (Ctrl+K takes the old title to it); and refuses GET /api/v1/metrics/history, and the same history over COM, to a credential confined to named domains, such as a domain administrator, the auditor role given a list of domains or an API key issued for named domains. With IMAP and POP3 now taking the client's address from a PROXY protocol header as SMTP does, the High Availability Runbook says that a balancer's /readyz check must not carry that header: the metrics listener reads none and answers a request behind one 404. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) publishes registry.gitlab.com/hmailserver/hmailserver:6.3.8, also tagged 6.3 and latest. See the 6.3.8 announcement. This topic covers what hMailServer 6.3.4 offers a monitoring system: the metrics listener's Prometheus exposition and health probes, the repository's Grafana dashboard, the OpenTelemetry push, the server's own metric history, the new SNMPv3 agent and, briefly, the daily block-list check of the server's own addresses. The one thing to know first: the metrics listener is off until you give it a port, and once it is on, the health check to configure is /readyz, because it is proved by a real database round trip rather than by a flag. The metrics listener One small HTTP listener serves both the exposition and the probes. Its seven settings are [Settings] keys, set on the Control Panel's Monitoring & troubleshooting → API & monitoring page (Monitoring & alerts since 6.3.7; Windows only), with PUT /api/v1/settings/ini/{name}, with hmctl ini set, or, on a Linux server whose REST listener is not yet on, with hmailserver --set-setting NAME=VALUE; the topic on settings explains where they are stored and when a save applies. All seven are taken when the listener starts, the three credential settings included, so a saved change waits for a reinitialise (POST /api/v1/server/reinitialize, or Apply now in the Control Panel or the Deck) or a service restart. A token that has been saved and not applied leaves /metrics answering as it did before. Setting Default Effect When it is wrong MetricsServerPort 0 0 means the listener is never created No port, no probes, no metrics, and no complaint anywhere MetricsServerBindAddress 127.0.0.1 An IP literal only, parsed with inet_pton: 0.0.0.0, 127.0.0.1, :: or a specific IPv6 address A host name or localhost is rejected: the log says MetricsServer: Invalid bind address, the listener does not start and the probes go with it MetricsServerAuthToken empty Sent by the scraper as Authorization: Bearer <token> Absent on a non-loopback bind: /metrics answers 503, not 401, and the body names the settings that would open it. The probes keep answering MetricsServerAuthUsername and MetricsServerAuthPassword empty HTTP Basic; with both schemes configured, either satisfies the check As above MetricsServerCertificateFile and MetricsServerPrivateKeyFile empty Both set makes the whole port HTTPS, probes included Set but unusable: the listener still serves the probes in the clear and /metrics answers 503 :: serves both address families; a specific IPv6 literal serves IPv6 only. "Loopback" means the whole of 127.0.0.0/8 and ::1. The probes are answered before any branch that can refuse. A Kubernetes httpGet probe, an ELB or ALB health check and a Docker HEALTHCHECK have nowhere to keep a bearer token. Refused credentials on /metrics are counted in hmailserver_metrics_unauthorized_requests_total rather than logged. Nothing on the request path touches the database or the disk. A refresher thread probes the database every 5 s (every 30 s after a failure), refreshes the delivery-queue aggregates at most every 10 s and only when a scrape has asked for them, and re-reads the certificate files every 5 minutes. One exception since 6.3.4: the two message-store gauges are cached for an hour and recomputed by whichever caller finds them stale, so about once an hour a scrape can run select sum(messagesize), count(*) from hm_messages on the listener thread itself: on a large store, a slow answer to that scrape and to any probe queued behind it. It is a single accept loop serving one connection at a time. A request's header read is bounded at 5 s and a response write at 15 s. For a load balancer's check the High Availability Runbook gives an interval of 5 s and a timeout of 2 s; never point a sub-second health check at it. The three health probes No credential in any configuration; HTTP/1.0 responses that close the connection. Path 200 when Body Use it for /livez Always, if the listener answered; never 503 alive Liveness restarts. It checks nothing on purpose: a database outage must not get a healthy process killed /readyz State is Running and the database has proved within the last 20 s that it can answer; 503 otherwise ready, or not ready: <reason> Load-balancer and VIP routing /healthz The same conditions {"status":"ok\|unavailable","state":"...","database":"up\|down","uptime_seconds":N} Dashboards and human debugging Readiness is four conditions in order, each with its own reason: not ready: server not in running state; not ready: database not connected; not ready: database did not answer the last readiness probe; and not ready: database readiness probe has not completed recently. The last exists because a probe against a database that has stopped answering can hang for as long as DBConnectionAcquireTimeout allows, 60 s by default. Since 6.3.4 a server in maintenance mode answers 503 not ready: draining: POST /api/v1/server/drain enters it, GET reports {"draining","since","sessions","deliveries","drained"}, and DELETE leaves it. Start-up is a refused connection, not a 503: the listener is brought up only after the state has gone to Running. Shutdown is a 503 first, then a refusal: the state goes to Stopping before anything is torn down, so /readyz reports 503 while ShutdownDrainSeconds runs. The High Availability Runbook builds its active/passive design on this: both nodes have the listener on, with MetricsServerBindAddress=0.0.0.0 and a MetricsServerAuthToken, and the VIP is gated strictly on /readyz. The passive node's service is stopped, so its /readyz is refused and it is never routed to. Since the probes need no credential, a missing token shows as missing dashboards, not as a failed failover. Every metric family Thirty-eight metric families in 6.3.4 (thirty in 6.3.3). Counters are monotonic since server start; a restart resets them, which is what hmailserver_start_time_seconds is for. Mail flow Metric Type Meaning, and the trap in it hmailserver_processed_messages_total counter One per delivery pass started, not per message received: a message deferred three times and then delivered counts four hmailserver_messages_delivered_total counter Delivery passes that completed with no failed recipient hmailserver_messages_deferred_total counter Passes rescheduled: a temporary failure, greylisting, or a message the virus scanner could not examine (AVFailAction=1) hmailserver_messages_bounced_total counter Delivery-failure notifications queued to the sender; one can cover several recipients hmailserver_delivery_queue_messages gauge Rows in hm_messages in the delivering state hmailserver_delivery_queue_oldest_message_age_seconds gauge Age of the oldest queued message; 0 when the queue is empty. Alert on this, not on depth hmailserver_spam_messages_total, hmailserver_viruses_removed_total counter Messages classified as spam; viruses removed during delivery preprocessing hmailserver_store_bytes, hmailserver_store_messages gauge Since 6.3.4. The size and the number of the messages in hm_messages, recomputed at most once an hour hmailserver_domain_messages_received_total{domain}, hmailserver_domain_messages_sent_total{domain} counter Only present when MetricsPerDomainEnabled=1; only hosted domains are ever labelled Added in 6.3.4. hmailserver_country_blocked_total and hmailserver_country_blocked_by_country_total{country} count connections and sign-ins refused by a country policy, and hmailserver_spam_trap_hits_total counts hits on a declared spam trap. Three gauges cover the feedback loops' seven-day window: hmailserver_domain_complaints{domain}, hmailserver_domain_complaint_window_messages{domain} and hmailserver_domain_complaint_rate{domain}. They are present only when MetricsPerDomainEnabled is set and complaint processing is on. The rate gauge is a ratio, not parts per million: Complaints.md alerts on hmailserver_domain_complaint_rate > 0.001. Parts per million is the unit of the complaints.rate.high alert rule's threshold, so an expression written in parts per million against the gauge would never fire. Sessions, authentication and TLS Metric Type Meaning hmailserver_sessions{protocol} gauge Concurrent sessions; protocol is smtp, imap or pop3 hmailserver_auth_success_total, hmailserver_auth_failures_total counter A rising failure rate against a flat success rate is a password-guessing run hmailserver_tls_handshakes_total, hmailserver_tls_handshake_failures_total counter The mail protocols only; a failed handshake on the metrics port is never counted here hmailserver_tls_certificate_expiry_seconds{certificate} gauge notAfter as a Unix timestamp per configured certificate, re-read every 5 minutes; at most 128 series. Absent entirely when no certificate is configured. With no certificate row, the ACME certificate is labelled ACME (automatic) hmailserver_metrics_unauthorized_requests_total counter Always 0 when no credential is configured The server and the database Metric Type Meaning hmailserver_start_time_seconds gauge Uptime is time() - hmailserver_start_time_seconds hmailserver_build_info{version,architecture,database_schema_version} gauge Always 1; the information is in the labels hmailserver_state{state} gauge One series per state (unknown, stopped, starting, running, stopping and, since 6.3.4, draining), exactly one of which is 1 hmailserver_workqueue_depth{queue}, hmailserver_workqueue_blocking_tasks_waiting{queue} gauge Seven fixed queue values: Server queue, Maintenance queue, Asynchronous task queue, Name lookup queue, IOCPQueue, SMTP delivery queue, External fetch queue. Asynchronous task queue sends the final 250, so depth there is mail waiting to be acknowledged hmailserver_messagestore_missing_files gauge Always 0 unless MessageStoreConsistencyCheck=1 hmailserver_command_processing_seconds, hmailserver_db_query_seconds histogram Bucket bounds from 0.0001 s to 1.0 s, and from 0.001 s to 10.0 s; both emit the full _bucket, _sum and _count set hmailserver_database_connected gauge 1 when the database answered a real round trip recently hmailserver_database_probe_success_timestamp_seconds, hmailserver_database_probe_age_seconds gauge When it last answered, and how long ago on the server's own clock; /readyz reports 503 once the age passes 20 s hmailserver_db_connections{state} gauge busy and available; available pinned at zero is saturation hmailserver_db_slow_queries_total counter Statements slower than SlowQueryLogMilliseconds; 0 while that setting is 0 Scraping it scrape_configs: - job_name: hmailserver scrape_interval: 30s scrape_timeout: 10s metrics_path: /metrics authorization: credentials: "<the value of MetricsServerAuthToken>" static_configs: - targets: ["mail.example.com:9100"] Use scheme: https when the certificate pair is set, and keep scrape_timeout well under scrape_interval. The wiki gives nine alerting rules against these metrics, among them hmailserver_database_connected == 0 for 2m, hmailserver_delivery_queue_oldest_message_age_seconds > 3600 for 15m and hmailserver_tls_certificate_expiry_seconds - time() < 14 * 86400. The Grafana dashboard The repository ships hmailserver/docs/grafana-dashboard.json. Import it as it is and pick your Prometheus data source; the instance variable, from label_values(hmailserver_build_info, instance), selects the server. Its uid is hmailserver-overview; it refreshes every 1m and opens on now-24h. Its panels array has 19 entries: four row headers, five stat panels and ten time series. Every rate is over [5m] and every query is filtered by instance=~"$instance". Health: server state (hmailserver_state == 1), database, uptime, missing message files, and TLS certificate expiry, amber below 21 days and red below 7. Mail flow: delivery outcomes (delivered, deferred and bounced, stacked), queue depth with the oldest message's age on a right-hand axis, messages processed with spam and viruses removed, and active sessions by protocol. Authentication and TLS: authentication outcomes and TLS handshakes, as rates. Latency and database: command processing and database query latency (p50, p95 and p99 by histogram_quantile over the two histograms' _bucket series), the connection pool by state, and slow queries. What the dashboard does not draw: the two database probe gauges, the two work-queue gauges, hmailserver_metrics_unauthorized_requests_total, the per-domain counters, the two store gauges and the six families added in 6.3.4. hmailserver_build_info is read by the instance variable and by no panel. The metric history inside the server The server keeps a short history of its own, so the Control Panel's dashboard can show 24 hours, 7 days and 30 days without Prometheus. MetricsHistoryDays (default 7; 0 switches it off) is the days kept in hm_metricsamples: one row per metric per minute, plus a sample at service start. Fourteen metrics are stored in 6.3.3, named as the exposition's without the hmailserver_ prefix and with the label folded in, such as sessions_smtp and processed_messages_total. 6.3.4 adds store_bytes and store_messages, whose history begins the day the server first samples them, and country_blocked_total and spam_trap_hits_total. Read it with GET /api/v1/metrics/history?metric=…&range=24h|7d|30d, or on Windows over COM with Utilities.GetMetricHistory. It has no labels and no histograms and is not a replacement for Prometheus, but it survives a restart. Pushing to an OpenTelemetry collector Three OTLP/HTTP signals, each a no-op until its own endpoint is set. The pushed metric names are the Prometheus names verbatim. Setting Default Effect OtelMetricsEndpoint empty OTLP metrics; the default path is /v1/metrics OtelMetricsInterval 60 Seconds between exports, clamped to 5–3600 OtelEndpoint empty OTLP traces only: one span per delivery attempt, joined to the trace named by the message's own traceparent; default path /v1/traces OtelLogsEndpoint empty OTLP logs: the same lines, categories and mask the log files get; default path /v1/logs What is pushed: the twelve counters that predate 6.3.4 and, since 6.3.4, hmailserver_spam_trap_hits_total, sessions as one gauge with a protocol attribute, hmailserver_messagestore_missing_files, and the per-domain counters when they are enabled. The histograms, the database and queue gauges and certificate expiry are scrape-only. On Linux, in every release from 6.3.0 to 6.3.3, a collector that accepted the connection and never answered held the exporter, and the stop that waits for it, for good; 6.3.4 fixes that. The SNMPv3 agent (since 6.3.4) For a monitoring system that speaks SNMP and not Prometheus or OpenTelemetry, 6.3.4 adds a read-only agent answering the mail MIB (RFC 2789, MTA-MIB) and the standard SNMP groups from the same counters /metrics serves; an object /metrics has no counter for is answered noSuchObject, not made up. It speaks SNMPv3 with the User-based Security Model for one user at authPriv only (HMAC-SHA-256 and AES-128), and SNMPv2c as an option that stays off even when the agent is on. SNMPv1 is not answered, a Set is answered noAccess, and nothing is sent unasked: no traps, no informs. It is off as shipped, and an empty allow list answers nobody. Setting (hm_settings) REST key Default Meaning SnmpEnabled snmp_enabled off The whole agent; off, no socket is opened SnmpPort snmp_port 161 The UDP port SnmpBindAddress snmp_bind_address empty, every address 127.0.0.1 for a collector on the same machine; otherwise an address on a management network SnmpAllowedAddresses snmp_allowed_addresses empty Addresses and CIDR ranges, at most 64. Name the managers one by one: UDP does not prove a source address SnmpV3User snmp_v3_user empty The one SNMPv3 user SnmpV3AuthPassword, SnmpV3PrivPassword snmp_v3_auth_password, snmp_v3_priv_password (write-only) empty 8 to 64 ASCII characters each SnmpV2cEnabled, SnmpV2cCommunity snmp_v2c_enabled, snmp_v2c_community (write-only) off, empty The community crosses the network in the clear; an answer is never more than eight times its request SnmpContact, SnmpLocation snmp_contact, snmp_location empty sysContact and sysLocation They are on /api/v1/settings (the server group), on the Control Deck's Settings page under Server and in hmctl settings. There is no COM property for any of them, and in 6.3.4 no Control Panel page, so Windows and Linux are configured the same way; since 6.3.7 they are the SNMP tab of the Control Panel's Monitoring & alerts page on Windows, which 6.3.5 and 6.3.6 had on Logging. They can also be given as HM_PROPERTY_SnmpEnabled and so on in the environment, which reaches the database only when hmailserver --apply-environment is run (hMailServer.exe --apply-environment on Windows): the service does not read the environment itself. Each setting is read when the agent starts, at the next service start or Reinitialize. The two passwords and the community are stored protected, never read back, and not carried by a backup: enter them again after a restore. Snmp.md's example keeps every password off the command line. On the server, for a manager at 192.0.2.10: $ umask 077 $ cat > snmp.json {"snmp_allowed_addresses": "192.0.2.10", "snmp_v3_user": "monitor", "snmp_v3_auth_password": "...", "snmp_v3_priv_password": "...", "snmp_contact": "[email protected]", "snmp_location": "Rack 4, London", "snmp_enabled": true} ^D $ hmctl settings set --from-json snmp.json $ rm snmp.json $ hmctl api POST /api/v1/server/reinitialize On the manager, with Net-SNMP 5.9 or later, defVersion 3, the user (defSecurityName), both passphrases, defSecurityLevel authPriv, defAuthType SHA-256 and defPrivType AES go in ~/.snmp/snmp.conf, readable by you alone (chmod 600); snmpwalk mail.example.com 1.3.6.1.2.1.28 then walks the mail MIB. What it serves. In the MTA-MIB mtaGroupTable has one row per protocol the server runs: 1 SMTP, 2 POP3, 3 IMAP. What has a counter is answered: mtaStoredMessages is the delivery queue's depth, mtaTransmittedMessages is hmailserver_messages_delivered_total, mtaGroupOldestMessageStored is the oldest queued message's age, and mtaGroupInboundAssociations is hmailserver_sessions for the protocol. mtaReceivedMessages is noSuchObject, because no counter of messages received exists, and so are the volume, recipient and conversion columns. Sixteen scalars at 1.3.6.1.4.1.32473.1.2.<n>.0 carry /metrics series directly, among them hmailserver_state as a number at 14 (3 is running, 5 draining) and the two delivery-queue gauges at 15 and 16. The enterprise number is a placeholder. 1.3.6.1.4.1.32473 is the number RFC 5612 reserves for documentation and examples; it is not registered to this project with IANA. When a number is registered, sysObjectID, the scalars and the engine ID's prefix move to it in one release, and a manager configured for these OIDs will need them changed. There is no MIB module file yet, for the same reason. When it does not start. Error 7420: the port could not be bound, because another program holds it (the operating system's own SNMP service, on Windows or Linux, listens on 161 when it is installed) or, on Linux outside the packaged unit, the process lacks CAP_NET_BIND_SERVICE; the server runs on without the agent. 7421: the engine's seed or boot count in hm_settings could not be read or written. 7422: an exception left the agent's thread; a Reinitialize starts it again. On Linux the packaged hmailserver.service has CAP_NET_BIND_SERVICE for port 25, which is what lets it bind 161. No container image of 6.3.4 is published; a container built from the repository's Dockerfile publishes the port as UDP (-p 161:161/udp) and needs a host name of its own, since two started with one fixed hostname are one SNMP engine. The daily block-list check, briefly Since 6.3.4 the server looks the addresses it sends from up on DNS block lists once a day and raises the reputation.listed alert when one is listed. The watch is on in a new installation and off after an upgrade, and is read at GET /api/v1/reputation; the topic on DKIM rotation, reports and the reputation watch has the lists, the addresses checked and what a failed lookup means. Where to read more Wiki: Monitoring and Health and High Availability Runbook Documents at v6.3.4: Snmp.md, HighAvailabilityRunbook.md, ReputationWatch.md, Complaints.md and the dashboard itself, grafana-dashboard.json The public documentation: 16.3 Prometheus metrics and health probes On this forum: Alerts, the audit trail, syslog and the journal, and per-domain reports, Reading the logs, and Warm standby and high availability for the pair the probes gate. See also The Control Deck and hMailServer on Linux: what you get and what you do not
  • 0 Votes
    1 Posts
    115 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.6 (6 October 2026) carries the PowerShell module, HmailServer.psd1 and HmailServer.psm1, Authenticode-signed, as files of the release, and Linux packages for x86-64 and AArch64; hmctl as a PyPI package and the module on the PowerShell Gallery are built but not yet published. See the 6.3.6 announcement. hMailServer 6.3.7 (17 October 2026) carries the module the same way, and since 18 October 2026 the repository and its releases are also mirrored on GitHub, issues and merge requests staying on GitLab. 6.3.7's REST API refuses a state-changing request that carries a credential when a browser reports it as cross-site, which hmctl and the module, sending no such header, never meet, and it refuses a key restricted to named domains a domain's rename, a domain alias and a custom role's creation, change or delete. See the 6.3.7 announcement. Since 21 October 2026 hmctl is on PyPI as hmailserver-cli and the module on the PowerShell Gallery as HmailServer, each at 6.3.7. hMailServer 6.3.8 (2 November 2026) carries the module as files of the release, as before, and both are sent 6.3.8 by a job started by hand once the release is published. 6.3.8's server answers 400 to a body that is not one JSON object or names a member twice, so a body given to hmctl api or --from-json must be one JSON object that names each member once, and 6.3.8's hmctl refuses a body holding NaN or Infinity before sending it. See the 6.3.8 announcement. hMailServer 6.3.4 has two command-line clients: hmctl for any shell, and the HmailServer module for PowerShell. They share one vocabulary and one API, so both administer a Windows server, a Linux server or a server on another machine, and neither needs COM or the Control Panel. This guide covers where each comes from, the credentials they take, the verbs, the import of accounts from a CSV file with its dry run, the configuration as one document, and what neither client does. The one thing to know first: both speak the REST API, so the REST listener has to be on before either can do anything, and neither client can turn it on. Neither is a replacement for the administration programs. The Windows Control Panel is the place to configure a server on Windows, and the browser Control Deck is that place on Linux. A command line is for what a command line is better at: making fifty accounts from a spreadsheet, reading one setting in a script, watching the delivery queue, or doing the same thing to twenty servers. Where the clients come from File What it is hmailserver/source/Tools/Cli/hmctl One Python 3 file with no dependencies beyond the standard library. hmailserver/source/Tools/Cli/HmailServer.psm1 The PowerShell module: Get-HmDomain, New-HmAccount, Set-HmSetting and the rest, answering objects rather than text. build/check-cli.py Runs both against a recorded server in CI and asserts what each one sent, so the two cannot drift apart. On Linux, the packages install hmctl as /usr/bin/hmctl. The 6.3.4 packages are x86-64 only, and the changelog records Python 3 as one of the .deb's two Recommends, the other being 7-Zip. On Windows, the wiki says the installer carries neither client: take them from the repository directory hmailserver/source/Tools/Cli/. Both clients are in the repository since 6.3.4. build/hmconfig.ps1 is a different tool and stays. It exports the whole configuration as one JSON document and applies it to another machine over COM, on Windows. The two clients here are per-object verbs over HTTP. Before anything else: the listener On Linux, store the port on the server itself and restart the service. Since 6.3.4 the database is the settings store, so editing the [Settings] section of hMailServer.ini does not turn the listener on once the server has stored the key. sudo hmailserver --set-setting RestApiPort=8080 sudo systemctl restart hmailserver On Windows, the Control Panel does this over COM. On either platform, TLS is required unless the listener is bound to the loopback, so give it a certificate the client trusts if it is to be reached from another machine. The REST API guide has the listener's settings, the reinitialise they wait for and the three ways it refuses to start, and the hmailserver command line on Linux has the checks --set-setting makes and how it takes a secret. Credentials Either the administrator password, sent as HTTP Basic with the user name Administrator, or an API key, sent as a bearer token. An API key is the better answer for anything unattended: it can be read-only, it can be restricted to one domain, and it can be revoked without changing the administrator password. export HMAIL_URL=https://mail.example.com:8045 export HMAIL_API_KEY="$(cat /etc/hmailserver/api.key)" hmctl domain list Connect-HmServer -Url https://mail.example.com:8045 -ApiKey (Get-Content .\api.key) Get-HmDomain hmctl reads HMAIL_URL, HMAIL_API_KEY, HMAIL_PASSWORD and HMAIL_PASSWORD_FILE, and takes --url, --api-key, --password and --password-file before the verb or after it. With no credential at all it asks for one, but only when there is a console to answer. In a scheduled task or a pipeline it says what is missing and stops. --insecure is refused for anything but the loopback. A self-signed certificate on localhost is the ordinary case and nothing is on the wire. An administrator password sent to a remote host over a connection nobody verified is what the refusal prevents, and the remedy is a certificate the client trusts. Some commands take the administrator password and never an API key, because no key reaches the routes behind them: hmctl apikey, hmctl ini, hmctl config changed, hmctl webhook and hmctl webhooks. The command synopsis names no option for a one-time code. Since 6.3.4 a one-time code is taken once, and the changelog's advice to a script that sent the administrator's password and a code with every request is a session or an API key; the REST API guide has the detail. hmctl hmctl [--url URL] [--api-key KEY | --password PASSWORD | --password-file FILE] [--insecure] [--json] <resource> <verb> [arguments] The resources, grouped. CommandLine.md has a row for each. Area Resources Notes Domains and what is in them domain, account, alias, list, group, fetchaccount, app-password, device, mailbox An account is listed and created under --domain, and read and written at its address, which is how the API is shaped. mailbox renames or moves one. Server-wide objects rule, route, port, iprange, certificate, journalrule list, create, update, delete Mail in motion queue, quarantine queue has list, retry, delete; quarantine has list, release, delete Settings settings, ini, config A settings group whole or one setting; the keys that were the [Settings] section; the configuration as one document Deliverability dkim, dmarc, suppression, bounce, bounces, complaint, complaints, spamtrap, traphits, reputation, openpgp reputation exits 1 when an address is listed and when a list could not be asked Integrations apikey, webhook, webhooks All three with the administrator password only, never an API key Operations log, backup, report, sample-domain, match match says what a rule criterion would decide, without a rule Bulk accounts import and export, a CSV file, with a dry run Anything else api A method and a path: the escape hatch, for a route newer than the client A few whole commands: hmctl domain create --set name=example.com --set active=true hmctl account create --domain example.com --set [email protected] --set password=... hmctl account list --domain example.com hmctl settings get antispam use_spf hmctl settings set antispam --set use_spf=true --set spam_mark_threshold=5 hmctl device [email protected] list hmctl queue list --json | jq '.[] | select(.retry_count > 3)' hmctl match contains viagra "cheap viagra here" hmctl report traffic --from 2026-09-01 --to 2026-09-15 --domain example.com hmctl api POST /api/v1/server/reinitialize How values are read. --set name=value reads true, false, null, numbers and anything starting with { or [ as JSON, and everything else as a string. --set-string keeps a value that looks like one of those a string. --from-json FILE takes a whole object, and - reads it from standard input. Output and exit codes. Output is a table by default and the server's own JSON with --json. Exit code Meaning 0 Success 1 An error 2 A usage mistake 3 The server refused the credential 4 What was named does not exist A script can therefore tell "no such account" from "wrong password" without reading the text. The PowerShell module Import-Module .\hmailserver\source\Tools\Cli\HmailServer.psm1 Connect-HmServer -Url https://localhost:8045 -Insecure Get-HmDomain | Where-Object active -eq $false Get-HmAccount -Domain example.com | Sort-Object max_size_mb -Descending | Select-Object -First 10 New-HmAccount -Domain example.com -Address [email protected] -Password (Read-Host -AsSecureString) Set-HmAccount -Address [email protected] -Property @{ max_size_mb = 500 } Set-HmSetting -Group antispam -Property @{ use_spf = $true } Get-HmQueue | Where-Object retry_count -gt 3 | Start-HmQueueDelivery Get-HmChangedSetting | Format-Table store, name, value, default Every function answers objects, so Where-Object, Sort-Object and Export-Csv work on them, and nothing in the module formats anything. Everything that changes the server supports -WhatIf and -Confirm, and deleting a domain or an account asks before it acts unless told not to. A domain name or an address completes from the server once Connect-HmServer has run. Invoke-HmApi -Method GET -Path /api/v1/... is the escape hatch, and is what every other function is built on. Accounts from a CSV file hmctl accounts import new-people.csv --dry-run hmctl accounts import new-people.csv hmctl accounts export example.com accounts.csv Import-HmAccount -Path .\new-people.csv -WhatIf Import-HmAccount -Path .\new-people.csv Export-HmAccount -Domain example.com -Path .\accounts.csv The file is UTF-8 (in Excel, CSV UTF-8) and its header row names the columns. These are the columns, and there are no others. Column Required Notes address yes Created under the address's own domain. password yes Sent as text, so digits alone are still a password. Never trimmed: white space at either end is refused. Judged by the server's password policy and, when it is switched on, the breach check. first_name At most 60 characters, counted as UTF-16 code units. last_name As first_name. active true, yes, on or 1; false, no, off or 0. Default true. max_size_mb A whole number of megabytes; 0 is no limit, and is the default. admin_level user, domain or server. Default user. address,password,first_name,last_name,active,max_size_mb,admin_level [email protected],S0me-Long-Passphrase,Ann,Adams,true,500,user [email protected],An0ther-Long-Passphrase,Bob,"Brown, Jr",true,,domain The columns may come in any order and their names in any case. Every cell but the password is trimmed, and an empty cell is not sent, so the server's default applies. A value with a comma in it goes in quotes. A row with more cells than the header has columns is refused rather than shifted along. What is refused before anything is sent. Each row becomes one POST /api/v1/domains/<domain>/accounts, and that route refuses a field it does not know by name. So a column that is not in the table is refused before anything is sent, a name column included. The clients also refuse, in the words the server would use: a row with no password; an active that is not one of the eight words; a max_size_mb that is not a whole number; an admin_level that is not one of the three; a name past its 60 characters; a password with a space or a tab at its start or end, which is the one row the clients refuse and the server would take. What a dry run cannot foresee. The password policy, the domain's account limit and an address the server does not accept are judged by the server alone. They are refused per row when the import runs, and listed with the address and the server's reason; hmctl adds the line number. An address that already exists is left alone and counted, never overwritten. An import that reset a password without saying so would lock people out with one command. --dry-run makes no request that changes anything and prints what would happen, line by line. The exit code is 1 when any row was refused. Who may make an administrator. admin_level is decided by who is asking. The administrator password, or a key issued for every domain, may make a server administrator. A key restricted to named domains may make users and domain administrators, and a row asking it for server is refused with a 403. The export writes the same columns less password, because the server does not have the passwords to give. Add a password column and change the addresses, and the exported file imports into another domain or another server as the same accounts: names, sizes, levels and whether each is active. The configuration as one document hmctl config changed # every setting that is not the shipped default hmctl config export server.json # the whole configuration, as a document hmctl config diff server.json # what would change; exit 1 if anything would hmctl config apply server.json # the plan, and nothing else hmctl config apply server.json --force # the plan, applied The document holds the settings groups, the hMailServer.ini keys the server exposes, the domains with their accounts, aliases and distribution lists, the groups, the rules, the routes, the listeners and the IP ranges. It is sorted throughout and carries nothing the server allocated, so two exports of an unchanged server are the same bytes. apply is a plan unless --force is added, and a deletion needs --allow-delete on top of that. Without it, an entry the server has and the document does not is left alone and counted. Not carried Why Passwords The server does not give them out. An account in the document that does not exist on the server is refused rather than created with a password nobody chose; add a password field for the accounts to be created. The secrets among the ini keys Listed as (set) or empty, and left out of the document. hmctl ini get <name> reads one by name. Certificates and the directories They are files and paths on one machine's disk. The journaling rules An address chosen for one server is almost never the right one for another. hmctl journalrule edits them. What lives under an account Application passwords, fetch accounts and folder permissions are the account holder's. The selector and key files of a domain whose DKIM key rotation is on The rotation moves them on its own schedule. Settings the environment manages In a container the value is the deployment's. The server refuses a change to such a setting with 409. Remote domain policies and alert rules The wiki's own addition: they have routes of their own that config does not read. What this server has changed from stock. hmctl config changed, or Get-HmChangedSetting in PowerShell, lists every setting whose value is not the one this version ships with, with the shipped value beside it. Settings: what is not stock has the route behind it, the other places to read the same list, what "default" means and who may see it. $ hmctl config changed STORE NAME VALUE DEFAULT ini LogLevel 5 9 ini SMTPDMaxTimeout 600 1800 ini OAuth2HmacSecret (set) ini DnsServer 10.0.0.1 (forced from [SettingsOverride]) database smtpnoofretries 6 4 5 of 391 settings are not what this version ships with. A secret is named and never shown. A value that the file's [SettingsOverride] section is forcing says (forced from [SettingsOverride]), a value the environment sets says (set by the environment), and a name the server holds a value for and ships no default for says (none: this server does not read it). A difference is not an exit code here, unlike config diff, because every real server has changed something. Reports and country blocking from a shell hmctl report sections hmctl report summary --from 2026-09-01 --to 2026-09-15 hmctl report senders --domain example.com --top 25 hmctl report mailboxes --csv mailboxes.csv hmctl report traffic --csv - --csv fetches the server's own CSV with format=csv instead of building one from the JSON. Per-domain reports has the sections, their sources and parameters, and who may read them. Run hmctl report sections first. It says which sources are recording and what this server cannot answer. The four sections counted from the message trace are empty when the trace is switched off, which is the default, and that is not the same statement as "nothing happened". Without --csv, the section's own note is printed above the table. CountryBlocking.md shows the country policy, which the topic on allow and block lists and country blocking explains, set from both clients: hmctl settings get settings geoip_database_status hmctl settings set --set geoip_database_file=/var/lib/GeoIP/GeoLite2-Country.mmdb hmctl settings set --set country_policy_smtp=block:CN,RU hmctl domain update example.com --set active=true --set country_policy=allow:GB,IE Set-HmSetting -Property @{ geoip_database_file = '/var/lib/GeoIP/GeoLite2-Country.mmdb' country_policy_smtp = 'block:CN,RU' } (Get-HmSetting).geoip_database_status Set-HmDomain -Name example.com -Property @{ active = $true; country_policy = 'allow:GB,IE' } geoip_database_status is read-only. A settings change is all or nothing, so a policy the grammar refuses leaves every other key in the same request unapplied. What neither client does Neither reads or writes the message store. Mail is IMAP's business. Neither is a backup tool of its own. hmctl backup start, hmctl backup restore and hmctl backup passphrase ask the server; the archive is written, read and restored by the server. Neither sets the administrator password. hmailserver --set-admin-password on the server does that, on Windows hMailServer.exe --set-admin-password, with the password in HM_ADMIN_PASSWORD or on standard input and never as an argument. On Windows the Control Panel does it too, but only where the service may write hMailServer.ini, as under LocalSystem: under NT SERVICE\hMailServer, a new 6.3.4 installation's account, its Administrator password card is refused with a sentence. Neither makes or carries the stored-secrets key. That is done on the server itself, never over the API. Neither can turn the REST listener on, since both need it. Neither sets what the environment sets in a container. No container image of 6.3.4 is published. Neither invents a field. A field that was not given is one the server decides. hmctl has no pending verb. SettingsApply.md notes that GET /api/v1/server/settings/pending is one request; hmctl api takes a method and a path. Where to read more The operator document: CommandLine.md, with a row for every resource and sections on DKIM rotation, bounces, complaints, DMARC reports, OpenPGP keys, the mailbox move and setting profiles. The wiki page: Command Line. For the examples above: ChangedFromStock.md, Reports.md and CountryBlocking.md. The public documentation: chapter 24, The REST API, which both clients speak, and 35.10, Administering a Linux server. On this forum: the Linux guide, The Control Deck, the 6.3.4 announcement, the REST API guide and, for what hmctl webhook manages, the send API and webhooks.
  • 0 Votes
    1 Posts
    124 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) changes three things this guide relies on. A Control Panel signed in to a server on another computer no longer reads or writes its own computer's hMailServer.ini for the [LDAP] values, which it cannot read from the server, where it showed that file or the defaults as the server's; the sidebar is regrouped, and Ctrl+K takes every old page title to its page. With SMTP logging on, 6.3.6 and every earlier version that offered them wrote the bearer token of AUTH XOAUTH2 and AUTH OAUTHBEARER and the lines of SCRAM-SHA-256, OAUTHBEARER and GSSAPI into the SMTP log in the clear; 6.3.7 hides every line of a SASL exchange but the user name of AUTH LOGIN and the authentication identity of AUTH PLAIN. An account given the serveradmin role with a list of domains is, over COM as over REST, confined to those domains and no longer the server's administrator, so a script that runs the directory synchronisation's preview and apply as such an account needs the role given with no list. 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, for the name an OAuth2 bearer token carries as for a password (work item 104), so a name that differs from the stored address in more than the case of the letters A to Z, or has white space at either end, may no longer sign in. On Windows an OAuth2PublicKeyFile whose path holds characters outside ASCII may be refused with HM7536 (one outside the code page, which worked up to 6.3.7, is refused), and then the key verifies no token: give the file, and the folders above it, an ASCII name before upgrading. See the 6.3.8 announcement. This topic covers three ways an organisation's directory or identity provider vouches for somebody on hMailServer 6.3.4's mail protocols: checking a password against Active Directory or another LDAP directory, creating mailboxes from that directory, and accepting OAuth2 bearer tokens. Know this first: 6.3.4 keeps settings in the database, but the [LDAP] section stays in hMailServer.ini on each server, with no REST route and no environment name; the server re-reads it within two seconds of a change, with no restart. The OAuth2 keys are ordinary [Settings] keys, stored like any other. Three mechanisms, three questions Mechanism The question it answers Control Panel page (Windows) LDAP directory authentication Is this the password of the directory user the account is linked to? Access & abuse protection → Directory authentication (LDAP) Directory synchronisation Which mailboxes should exist, according to the directory? Access & abuse protection → Directory synchronisation OAuth2 bearer tokens Did the identity provider sign this token, and which mailbox does it name? Access & abuse protection → Authentication All three are off by default, and an upgrade switches none of them on: the [LDAP] section is absent until you add it, and OAuth2Enabled defaults to 0. Where the [LDAP] section lives Windows. The Control Panel's page writes the hMailServer.INI of its own machine. Connected to another machine, its Save is disabled, although "the connection test still works with the values typed here". Work on the server, over Remote Desktop. Linux. No Control Panel and no COM. The file is /etc/hmailserver/hMailServer.ini, 0640 root:hmailserver: sudoedit /etc/hmailserver/hMailServer.ini. Not REST or hmctl. The REST ini routes and hmctl ini set handle [Settings] keys only. Per server. On a warm standby each node has its own [LDAP]; keep the two alike by hand. ServicePassword is a line of that file. It is not among the [Settings] secrets 6.3.4 seals under the stored-secrets key; the Control Panel's field for it is write-only. The sign-in keys Key Default What it does Enabled 0 The master switch Server empty The directory host; required Port 0 0 means 636 for LDAPS, 389 otherwise Security 2 2 LDAPS, 1 StartTLS, 0 plain; anything else is LDAPS VerifyCertificate 1 Check the directory's certificate AllowUnprotectedPassword 0 Permit a simple bind over a plain connection BindMethod 0 0 simple; 1 Negotiate (Kerberos or NTLM through Windows SSPI) SearchBase empty Required unless UserDnTemplate is set or the bind is Negotiate UserSearchFilter (&(objectCategory=person)(objectClass=user)(sAMAccountName=%u)) How the user's entry is found UserDnTemplate empty Bind directly, without searching ServiceUsername, ServiceDomain, ServicePassword empty What the search binds with TimeoutSeconds 10 Bounds every directory operation; 1 to 90 FallbackToWindowsLogon 0 Also try Windows LogonUser; Windows only LDAPS with VerifyCertificate=1 is the default and the recommended configuration, proven end to end against a Windows Server 2025 domain controller. Server must be the name in the directory's certificate: the same setup pointed at the controller's IP address, with the right password, is refused, and HM5920 reports a failure "unrelated to the credentials supplied". On Windows, put the issuing CA in LocalMachine\Root; when this was proven the service ran as LocalSystem, and a CurrentUser\Root import served it nothing. The bind needs no extra privilege under NT SERVICE\hMailServer, which a new 6.3.4 installation runs as. On Linux the certificate must chain to the system trust store. StartTLS (Security=1) connects on 389 and upgrades before anything is sent. Plain LDAP is labelled Unprotected LDAP - cleartext (lab networks only): there a simple bind is refused (HM5921) rather than sent, unless AllowUnprotectedPassword is 1. Simple or Negotiate. A simple bind sends the password to the directory, so it belongs on LDAPS or StartTLS. Negotiate, Windows only, never sends it; it worked on 389 against a controller that had no LDAPS certificate yet, and suits a mail server not joined to the domain. Search or template. A search binds with the service credentials, finds the user under SearchBase with UserSearchFilter, then binds as the user. It asks for two entries to see ambiguity: more than one match is a configuration fault and authenticates nobody. UserDnTemplate skips the search, so no service credential need be stored at all; with a simple bind, that is the configuration the feature was proven with. Linking an account to the directory The directory is asked only about linked accounts; others keep their own password. The webmail's sign-in shares the IMAP and POP3 logon, so it takes the directory password too. Control Panel: the account dialog's Windows directory tab, with the domain (ADDomain) and user name (ADUsername); Browse Active Directory searches the forest to fill them. Since 6.3.4 the tab has that name; it was called Directory, and the new Directory entry tab is the organisation's address book, unrelated to Active Directory. REST: PUT /api/v1/accounts/{address} with ad_enabled, ad_domain and ad_username. COM (Windows): IsAD, ADDomain and ADUsername. What a link changes. The account's stored password is no fallback: in the proof run, a linked account's local password was refused. Linked accounts bypass local hashing, so they are exempt from the minimum-hash policy and from password expiry, and cannot sign in with SCRAM-SHA-256; their clients use PLAIN or LOGIN over TLS. App passwords work for them: the directory holds the password, but an app password is local, which is what makes a second factor possible for a directory account at all. The person cannot change the password in the webmail (POST /api/v1/me/password answers 409), and Forgotten your password? cannot reset it, because this server does not hold it. Without LDAP, on Windows, a linked account is checked by LogonUser, which needs the mail server's host joined to the domain. On a host that is not joined, Windows answers every domain name, real or misspelt, as a wrong password, so the account can never sign in; HM5911 says so, naming the domain and the computer. An empty domain, . or the computer's own name means a local Windows account. Never leave the domain empty by accident: with [LDAP] Enabled off, an account linked with an empty domain and the user name administrator would accept the server's local Administrator password. FallbackToWindowsLogon never applies to a wrong password: a directory's rejection deliberately does not fall through to LogonUser, because that would double every failed attempt and trip a lockout policy on a single wrong password. Leave it off on a host that is not domain-joined. Setting it up on Windows For LDAPS or StartTLS, put the directory's issuing CA in LocalMachine\Root on the mail server. On the mail server, open Directory authentication (LDAP). Switch directory authentication on (Enabled); until then a linked account is checked by Windows LogonUser, not with these settings. Fill in the server, the security, the bind, and either the search or UserDnTemplate; the first card asks Is this configuration complete? Test the connection with a directory user name, its AD domain and its password. It tries the values typed on the page. Save. The server re-reads [LDAP] within two seconds. Link one account and sign in with the right password and a wrong one. Over POP3 the proof run got +OK Mailbox locked and ready and -ERR, and the log showed the bind and the rejection separately. Link the others by hand, or with directory synchronisation. Setting it up on Linux The only directory path is an LDAP simple bind over LDAPS or StartTLS, proven against OpenLDAP's slapd. Make sure the directory's certificate chains to the system trust store, and use the name it carries. Add the section with sudoedit /etc/hmailserver/hMailServer.ini, filling in the angle brackets. Without a UserSearchFilter line the search uses Active Directory's filter, (&(objectCategory=person)(objectClass=user)(sAMAccountName=%u)), which finds nobody in another directory such as OpenLDAP; there, write a filter that matches its user entries, with %u for the name: [LDAP] Enabled=1 Server=<the host name the directory's certificate carries> Security=2 VerifyCertificate=1 BindMethod=0 SearchBase=<the DN the users are under> UserSearchFilter=<that filter, unless the directory is Active Directory> ServiceUsername=<the search account> ServicePassword=<its password> Two seconds later the server has it. Link an account over REST (ad_enabled, ad_domain, ad_username) and sign in with the right password and a wrong one. Linux has no Negotiate (BindMethod=1 is refused as a directory failure, not a wrong password: HM6420, reported once), no LogonUser (a linked account while [LDAP] Enabled is off is refused with HM6421, which names the settings to set; FallbackToWindowsLogon refuses with HM6404), and no GSSAPI bind. When the directory cannot answer So that an outage does not read as users mistyping, infrastructure failures are reported apart from credential rejections, throttled per error and endpoint, and TimeoutSeconds bounds the wait. Code Meaning HM5920 The directory could not be used (a certificate not matching Server, say). Reported at High: unrelated to the credentials, and every account on that directory is affected HM5921 A simple bind would have sent the password unprotected, so the logon was refused first HM5911 Windows LogonUser: the host is not joined to the domain; at most once a minute per problem Directory synchronisation: preview, then apply Synchronisation reads the directory, works out which mailboxes should exist, and only when asked creates and updates them; it was verified end to end against a Windows Server 2025 domain controller. The planner writes nothing and the applier decides nothing, so a preview cannot promise what the apply would not do. Per-domain opt-in. A domain takes part only if its Active Directory domain name is set (ADDomainName in the domain dialog, ad_domain_name over REST), so a search base one level too high cannot provision into unrelated hosted domains. Nothing is ever deleted, account or message. The most it does is clear Active, only when asked, and never on a truncated or empty enumeration or for a domain in which nothing was seen. No password. A made account is marked IsAD and signs in through the directory from the start. No existing account claimed. An account that is not linked, or is linked to another AD domain, is reported, not changed. Paged. Active Directory answers a search beyond MaxPageSize (1000) with the first page and a success status, so the enumeration pages; SyncMaxUsers is the ceiling. [LDAP] key Default SyncFilter (&(objectClass=user)(objectCategory=person)(mail=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2))) SyncMailAttribute mail; proxyAddresses works, preferring the capitalised SMTP: primary SyncUsernameAttribute, SyncDisplayNameAttribute sAMAccountName, displayName SyncMaxUsers 5000; at most 100000 SyncScheduleMinutes 0, off; 15 at least, 10080 (seven days) at most On Windows: Get authentication working first. Synchronisation binds as the service account, honouring BindMethod, so Negotiate works where there is no LDAPS certificate. Set the Active Directory domain name on each mail domain that takes part. Open Directory synchronisation, check the selection keys, choose the domain and whether absent accounts are marked inactive, and run Preview. It changes nothing. Read the report, including the existing accounts it reports and leaves alone. Apply. The button stays disabled until a preview of the same options has run. Behind the page are Settings.PreviewDirectorySync(DomainName, DisableMissing) and Settings.ApplyDirectorySync, for a server administrator and callable from a script. From a Control Panel on another machine the six selection boxes are read-only and, up to 6.3.6, show their defaults; since 6.3.7 the Control Panel no longer reads its own computer's file for the [LDAP] values, which it cannot read from the server, so it no longer shows them as the server's. Preview and Apply still run inside the server. The unattended schedule (SyncScheduleMinutes) runs at start and then every so many minutes. It creates and updates but never disables: unattended is where nobody reads the report that would show a mailbox about to be taken away. The schedule is one of the tasks the server registers as it starts, and only if SyncScheduleMinutes is set then, so restart the service once after first setting it. On Linux, with no COM, there is no preview or apply, and whether the schedule runs there is not documented: treat it as untested. OAuth2 bearer tokens on IMAP, POP3 and SMTP With OAuth2Enabled=1, SMTP, IMAP and POP3 offer XOAUTH2 and OAUTHBEARER (RFC 7628), by default only over TLS (OAuth2RequireTLS=1). The server verifies the token's signature itself, takes the login name from the claim OAuth2UsernameClaim names (email), and signs in to that mailbox; a user= the client asserts must match. The mailbox must already exist: a token never makes one. Key Default What it does OAuth2JwksUrl empty The provider's JWK Set; the token's kid picks the key. https, or plain http to loopback only OAuth2JwksCacheSeconds 3600 How long the set is kept OAuth2PublicKeyFile empty A PEM RSA or EC public key; the fallback OAuth2HmacSecret empty The HS256 key OAuth2AllowedAlgorithms RS256 HS256, RS256 and ES256 recognised; none never OAuth2Issuer, OAuth2Audience empty Expected iss and aud; empty turns the check off OAuth2IntrospectionUrl empty RFC 7662 endpoint asked whether a verified token is still active OAuth2IntrospectionClientId, OAuth2IntrospectionClientSecret empty Who this server introspects as, over HTTP Basic OAuth2IntrospectionCacheSeconds 300 Verdict reuse, never past the token's exp OAuth2IntrospectionFailOpen 0 1 accepts a token the endpoint cannot judge, logging each An unknown kid, which is what a rotation looks like, fetches the set again at once, at most once a minute; a failed fetch keeps the keys held. HttpProxy, when set, carries these fetches. Where to set them. Each applies when saved: on the Control Panel's Authentication page, with PUT /api/v1/settings/ini/{name} and the administrator password, with hmctl ini set, or with COM Settings.SetIniSetting. Not in hMailServer.ini: since 6.3.4 the database holds them, and a line edited in the file is not used; Settings: when a save takes effect explains the store. Choose the key source: OAuth2JwksUrl, so a rotation needs no copied file; or OAuth2PublicKeyFile; or OAuth2HmacSecret with HS256 allowed. Set OAuth2Issuer and OAuth2Audience to what the provider writes into its tokens. Left empty, neither is checked: any token the provider's keys signed is accepted, whatever client it was issued to. Optionally set introspection, so a revoked token stops working before it expires. Only then set OAuth2Enabled to 1. On Windows, read the OAuth2 / XOAUTH2 item of the Control Panel's External setup list. Why that order. Switched on without usable key material, the mechanisms are still advertised, every token fails, and failed bearer sign-ins feed the auto-ban, so a correct client can have its address banned. On Linux before the REST listener is up, hmailserver --set-setting stores a key; a secret is given by name alone and typed at a prompt. A value stored this way reaches the running server at its next save, reinitialise or restart. Set the issuer and audience too, as step 2 says, putting your provider's values in place of these examples: sudo hmailserver --set-setting OAuth2JwksUrl=https://idp.example.com/keys sudo hmailserver --set-setting OAuth2Issuer=https://idp.example.com sudo hmailserver --set-setting OAuth2Audience=hmailserver sudo hmailserver --set-setting OAuth2Enabled=1 sudo systemctl restart hmailserver Since 6.3.4: a bearer token meets the domain's and the account's country policy, as a password does (a stolen token used to sign in from a refused country); an SMTP session signed in with a token can send more than one message (the second used to be refused with 550 "Login credentials no longer valid"); and OAuth2HmacSecret and OAuth2IntrospectionClientSecret are sealed at rest once the installation has a stored-secrets key, which an upgrade does not make until an administrator runs --create-stored-secrets-key. Tested how. The regression fixtures play the identity provider on the loopback, to make it misbehave: RS256 and ES256, the JWK Set and its rotation, introspection and fail-open. A real provider's tokens are not yet part of them. This is inbound only. The OutboundOAuth2 keys and FetchOAuth2Hosts are the server's own token for relaying through and collecting from Microsoft 365; Routes, relays and smart hosts covers relaying with it. Where these end: SCIM, single sign-on and Kerberos The LDAP backend authenticates; SCIM provisions. Synchronisation reads the directory; SCIM is the other direction, Entra ID or Okta calling /scim/v2 under an API key whose role holds provision, and it removes accounts, which synchronisation never does. Single sign-on, OpenID Connect or SAML 2.0, opens the webmail and the Control Deck only, ending in POST /api/v1/session; the protocols' bearer tokens are a separate, older mechanism with the OAuth2 settings above, and single sign-on does not reach mail clients. On Windows, SASL GSSAPI (GssapiEnabled) lets a mail client sign in with a Kerberos ticket, mapped to the account linked to that directory user. SCIM provisioning and single sign-on sets those up; Signing in and Connecting a mail client show the person's side. What it does not do No Negotiate, Windows logon or GSSAPI bind on Linux. hMailServer on Linux lists the rest. No LDAP server here, and the webmail's organisation directory is typed in, not imported from Active Directory. Where to read more Wiki: Settings Reference sections 9.2 and 11, Capabilities and Configuration, Control Panel Reference, Installing on Linux, Security Hardening, Sign-in and Identity, Architecture Documents: SettingsApply.md, ServiceAccount.md, Scim.md, PasskeysAndSingleSignOn.md, TestLab.md The Roadmap rows on LDAP, which record how each part was proven On this forum: the stored-secrets key, auto-ban and the warm standby The public documentation's Creating accounts from Active Directory and Modern authentication Report a security flaw in any of this by SECURITY.md's routes: a confidential GitLab issue, or email to contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, which GitLab files as one.
  • 0 Votes
    1 Posts
    143 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) confines an account given the serveradmin role with a list of domains to those domains over COM as over REST, so it is no longer the server's administrator: give the role with no list to an account that is to administer the whole server, a script's or a Control Panel's account among them. Over COM, one given domainadmin with a list that leaves out its own domain no longer administers that domain's ordinary accounts; 6.3.4 to 6.3.6 allowed both where REST refused them. A role is given or taken away only by a credential that holds manage_roles in the account's domain and takes in what the account holds now and afterwards, and a credential confined to named domains is refused over COM, as over REST, a domain's rename, a domain alias, a relay host changed to an address that is not public and an action on a domain's or an account's rule that reaches one of the server's own addresses, a configured route or a function of the event script. Rule.Save and RuleAction.Save refuse, on a domain's or an account's rule, a Set header value of From, Sender, Resent-From, Resent-Sender or DKIM-Signature and a Reply from an address the owner may not send as, and on a rule of every owner a Set header value of the loop count; one stored before the upgrade is taken without that action, and the application log names it. For a rule of every owner they refuse a Run function that is not a plain function, which one stored before still meets when the rule fires, as since 6.3.4. Utilities.CheckSieveSyntax refuses a Sieve script that adds or deletes From, Sender, Resent-From, Resent-Sender, DKIM-Signature, Auto-Submitted or the loop count. Once the database has ended a Database object's transaction, every later statement is refused and Database.CommitTransaction fails: call Database.RollbackTransaction and run the whole transaction again, or stop. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) signs a name in over COM, as at every other sign-in, only when it is the account's stored address, compared in lower case after a domain alias and the default domain: a mailbox address spelt otherwise, in more than the case of the letters A to Z, or with white space at either end, may no longer sign in; it fails as a name that does not exist fails, and is counted as a failure. See the 6.3.8 announcement. This topic covers the COM API of hMailServer 6.3.4: what a script connects to and how it authenticates, the object model and the shape every collection shares, working PowerShell examples, the change 6.3.4 makes to when a configuration change takes effect, what 6.3.4 adds to the object model, administrator roles over COM and why API keys are not there, the changed-from-stock document, the COM surfaces of distribution lists and the directory, and the Import Tool. The one thing to know first is that the COM API is Windows-only, and that an object you add to a collection, or an item of one you change, is not stored until you call its Save(); a few writes need none, among them an account's SieveScript and Settings.SetIniSetting, and an address saved or deleted through Route.Addresses needs no Save() of the route. Since 6.3.4 the running server's routes, relays and other shared configuration objects also wait for that Save(); up to 6.3.3 they changed the moment a property was set. What you are connecting to hMailServer exposes a full COM API. Almost anything the Control Panel can do, a script can do, because the Control Panel does its work through COM. The exceptions are the pages that edit a file on the server itself: REST API keys (described under roles below), the two directory pages' [LDAP] section, the sending limits on Server limits & expert settings, the [Database] rows of the Performance page, and the event script; and Discovery, which reads the REST listener because eDiscovery has no COM interface. The COM API and the Control Panel are both Windows-only: a Linux server is administered over REST, hmctl and the Control Deck (hMailServer on Linux: what you get and what you do not). One type library, hMailServer, with 94 coclasses in 6.3.3 and 100 in 6.3.4. The root is Application, ProgID hMailServer.Application. Every interface is a dual IDispatch interface, so late binding works without registering the type library. CreateObject, New-Object -ComObject and C# dynamic all work on a machine where only the server is installed. Early binding depends on vtable order, so new members are always appended, never inserted. An interop assembly built against 6.3.3 keeps working; recompile it to reach the new members, and on a hand-copied installation register the server again, elevated, so that the new interfaces are known to COM. Late-bound scripts need nothing. The installer's admintools component exists only to register the type library, so that scripts on another machine can administer this one. # Local $app = New-Object -ComObject 'hMailServer.Application' # Remote, over DCOM, against a machine where admintools is registered $app = [Activator]::CreateInstance([Type]::GetTypeFromProgID('hMailServer.Application', 'mail01')) Authenticating $app = New-Object -ComObject 'hMailServer.Application' $account = $app.Authenticate('Administrator', 'your-admin-password') if (-not $account) { throw 'Authentication failed.' } Authenticate returns an Account object, or $null on failure. Sign in as Gets Administrator with [Security] AdministratorPassword from hMailServer.INI server administrator A mailbox address whose AdminLevel is 2 server administrator A mailbox address whose AdminLevel is 1 domain administrator: their own domain only A mailbox address that holds a role (since 6.3.4) what the role's permissions reach, in the domains it reaches An empty administrator password is an open door. If the administrator password is empty, and no administrator TOTP secret is enrolled, every COM session starts already authenticated as a server administrator. This is the historical behaviour. A second factor changes the call you must make. With Settings.EnrolAdministratorTOTP() used, Authenticate refuses the administrator outright and AuthenticateWithCode is the only way in: if ($app.AdministratorTOTPEnabled) { # readable BEFORE authenticating $app.AuthenticateWithCode('Administrator', $password, $code) | Out-Null } else { $app.Authenticate('Administrator', $password) | Out-Null } Three things about that sign-in change in 6.3.4: Change, since 6.3.4 What a script sees A one-time code is taken once a TOTP code the server has accepted is refused if it comes again, for the administrator and for an account alike; the refusal is not counted as a wrong code A wrong code is counted AuthenticateWithCode checks the password first, and a wrong code after a right password counts against a budget of five in fifteen minutes, then no code is taken for fifteen An app password is not a COM logon Authenticate and AuthenticateWithCode answer an app password as they answer a wrong password; it still opens the mailbox over IMAP, POP3 and SMTP How a refusal reaches you. If you are not entitled to an object, every member returns an error HRESULT: a terminating exception in PowerShell, a trappable error in VBScript, a COMException in C#. Up to 6.3.3, seventy-two COM classes answered a refusal as a bare Exception from HRESULT: 0x800403E9. Since 6.3.4 a refusal arrives with the server's own sentence. The object model From Application What is there Domains Domain, and under each Accounts, Aliases, DistributionLists, DomainAliases and, since 6.3.4, Rules Account (under Accounts) Address, Password, Active, MaxSize, Size, AdminLevel; Messages, IMAPFolders, Rules, FetchAccounts, AppPasswords; SieveScript, TOTPEnabled Settings the server-wide properties and the sub-objects AntiSpam (which owns Quarantine), AntiVirus, Backup, Cache, Directories, Groups, IncomingRelays, Logging, MessageIndexing, PublicFolders, Routes, Scripting, SecurityRanges, ServerMessages, SSLCertificates, TCPIPPorts; since 6.3.4, RemoteDomainPolicies Rules the global rules Status UndeliveredMessages, StartTime, ProcessedMessages, RemovedViruses, RemovedSpamMessages, SessionCount Utilities helpers such as CheckSieveSyntax, CriteriaMatch, MakeDependent, PerformMaintenance, ImportMessageFromFileToIMAPFolder Database ExecuteSQL, ExecuteSQLWithReturn, RequiresUpgrade, IsConnected, DatabaseType BackupManager StartBackup, LoadBackup GlobalObjects DeliveryQueue, MessageTrace, Languages Links Domain(DBID), Account(DBID), Alias(DBID), DistributionList(DBID) Diagnostics DiagnosticResults, each a DiagnosticResult Every collection offers the same shape: Count, Item(index), ItemByDBID(id), Add(), Refresh(), Delete(index) and DeleteByDBID(id), with ItemByName or ItemByAddress where a name exists. Items have Save() and Delete(). Add() does not persist. It returns a new, unsaved object. Set the properties, then call Save(). A script that forgets Save() looks as though it worked and changes nothing. In PowerShell, keep the object in a variable rather than chaining. Working examples $app = New-Object -ComObject 'hMailServer.Application' $app.Authenticate('Administrator', 'your-admin-password') | Out-Null # Add a domain $domain = $app.Domains.Add() $domain.Name = 'example.com' $domain.Active = $true $domain.Save() # Add an account $account = $domain.Accounts.Add() $account.Address = '[email protected]' $account.Password = 'a-strong-password' $account.Active = $true $account.MaxSize = 1000 # MB, 0 = unlimited $account.Save() # Report on every mailbox foreach ($d in $app.Domains) { foreach ($a in $d.Accounts) { '{0,-40} {1,8} MB used' -f $a.Address, [math]::Round($a.Size, 1) # Size is already in MB } } Three units, three objects. Account.Size is megabytes. Dividing it by 1MB again, which older examples do, prints 0 for every mailbox. Message.Size is kilobytes, and the Message size rule criterion is bytes. Find one account without walking every domain: $domain = $app.Domains.ItemByName('example.com') $account = $domain.Accounts.ItemByAddress('[email protected]') Send a message from a script. Message is a creatable coclass in its own right, and Save() on a new one queues it for delivery: $m = New-Object -ComObject 'hMailServer.Message' $m.FromAddress = '[email protected]' $m.From = 'Postmaster <[email protected]>' $m.AddRecipient('Alice', '[email protected]') $m.Subject = 'Scheduled maintenance tonight' $m.Body = 'The server will restart at 22:00.' $m.Save() The queue, and a backup: $app.Status.UndeliveredMessages # tab-separated text, one row per message $app.GlobalObjects.DeliveryQueue.ResetDeliveryTime() $app.GlobalObjects.DeliveryQueue.StartDelivery() $b = $app.Settings.Backup $b.BackupDomains = $true; $b.BackupSettings = $true; $b.BackupMessages = $true $b.Destination = 'D:\backups' $app.BackupManager.StartBackup() The [Settings] keys are reachable generically: $app.Settings.IniSettingNames # every key the server knows $app.Settings.GetIniSetting('ManageSieveServerPort') $app.Settings.SetIniSetting('ManageSieveServerPort', '4190') Up to 6.3.3 a change this way usually needed a restart. Since 6.3.4 the keys are kept in the database, and Settings.IniSettingApplies(name) says whether a save applies a key at once, at a reinitialise or at a restart; Settings.PendingIniSettings lists the saved ones the server does not run on yet. Settings: when a save takes effect, and the three levels has the counts and what becomes of the file's copy. What changed in 6.3.4: a shared object takes effect at Save() Some collections are not loaded for your COM session alone: the running server reads the very same items while it works. They are the routes and their addresses, the incoming relays, the DNS black lists, the SURBL servers, the remote domain policies, the server messages, the blocked attachments, the SSL certificates and the groups. Up to 6.3.3, setting a property on one of those items wrote the live object every SMTP, IMAP and delivery thread was reading, which let a session read a string while it was being freed. Since 6.3.4: The first property you set makes a copy, held by your COM object, and every later set writes the copy. Reading a property back from the same object reads what you set. Nothing is in effect until Save(). Save() writes the copy to the database and the server reads the collection again. A property set and never saved changes nothing at all. Up to 6.3.3 it changed the running server until the next restart while the database kept the old value. A script that relied on that must now call Save(). An object read before the save keeps what it read. After a Save(), read the item again from its collection (ItemByDBID, ItemByName) to see the saved values. A route's addresses. A change to an existing address waits for that address's Save(), like every item here. An address saved or deleted through Route.Addresses reaches the running route at once, without the route's own Save(), as it always did. A RouteAddresses list read before the route's save is the old route's list, though an address saved or deleted through it still reaches the running route. A save the database refuses leaves the running item as it was, and your object keeps the refused values, to be corrected and saved again. A remote domain policy is the exception: it is read back as the database holds it. A save whose re-read fails still answers success, because the save succeeded, and is reported as error HM7390: the change is in the database and not yet in effect, until a reinitialise, a restart, or another save. The Control Panel reads each item fresh, sets, saves and reads again, so it is not affected. The other collections were never shared this way and behave as before: TCP/IP ports, security ranges, the white list, the greylisting lists, blocked senders, sender list entries, and everything loaded for the COM object itself (domains, accounts, aliases, lists, rules, fetch accounts, app passwords, folder permissions). What 6.3.4 adds to the object model Six new interfaces make 100 coclasses, and the IDL has 1,884 HRESULT declarations against 6.3.3's 1,375. The wiki page lists every member; this is the shape of it. Interface Added in 6.3.4 Application Roles, CurrentRole, CurrentPermissions, RoleAssignments, AssignRole, ResetPassword, UnlockAccount Settings RemoteDomainPolicies; GetChangedFromDefault, GetSettingDefault; IniSettingApplies, IniSettingAppliesAll, PendingIniSettings; the profiles (GetProfiles, ApplyProfile, RevertProfile and the rest) Domain Rules; the quota policy; the domain transforms and disclaimer; SenderList; the DKIM rotation members; SpamTraps Account MoveTo; GetDevices, RevokeDevice; RoleName, RoleDomains, RolePermissions; the nine Directory properties; ExportArchive; Kind (0 a person, 1 a meeting room, 2 a piece of equipment) Utilities among others GetAuditTrail, VerifyAuditTrail, RevertAuditedChange, GetSetupChecklist, MoveMailbox, RunDkimRotation DistributionList Membership, and the list manager's members BackupManager, BackupSettings LoadEncryptedBackup, RestoreMailbox, RestoreMessage; EncryptionPassphrase (write-only), EncryptionPassphraseSet New RemoteDomainPolicies and RemoteDomainPolicy; SenderListEntries and SenderListEntry; GreyListingBypassEntries and GreyListingBypassEntry eServerState gains hStateDraining (5). The rule criteria gain eFTAttachmentName (9), eFTSensitiveContent (10), eFTAttachmentType (11), eFTExternalRecipient (12) and eFTContent (13), and the rule actions eRAAddDisclaimer (11), eRAReject (12), eRARequireTLS (13), eRAQuarantine (14) and eRAWarnSender (15). Roles over COM, and why API keys are not Since 6.3.4, authority is a role: serveradmin, domainadmin, helpdesk, auditor or a custom one, given to an account for named domains. Nothing changes on the day you upgrade: an account with no assignment holds exactly what its AdminLevel has always given it. An account with a role signs in with its own address and password, and COM asks the same question REST asks. Application.CurrentRole and CurrentPermissions say what the session holds. Administrator roles end to end has the permissions, the domains a role reaches and the Control Panel pages each role sees. Giving a role over COM is Application.AssignRole("[email protected]", "helpdesk", "example.test"), with an empty role name to take it away. Account.RoleName, RoleDomains and RolePermissions read it back. Custom roles are made over REST (POST /api/v1/roles), and COM has no way to delete a role at all. A help desk and an auditor over COM. A help desk's script has Application.ResetPassword, UnlockAccount and GlobalObjects.MessageTrace; COM's settings objects answer only the server administrator, so an auditor reads the configuration through the Deck or REST. Refusals that do not give anything away. Application.ResetPassword and UnlockAccount answer No such account. alike for an account that does not exist and for one the role does not reach. Settings.ClearLogonFailureList answers the server administrator alone. The question 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) each 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. API keys are not a COM object. The API key store has never been COM-visible. The key store (/api/v1/apikeys) answers to the administrator password and to nothing else, and a key is given a role with POST /api/v1/apikeys and "role":"helpdesk". The Control Panel's REST API keys page does not go through COM either: it edits hMailServerApiKeys.ini directly. What this server has changed from stock Since 6.3.4, Settings.GetChangedFromDefault answers every setting whose value is not the one this version ships with, with the shipped value beside it: the same document, byte for byte, as GET /api/v1/settings/changed. Only the administrator password may read it; an API key of any scope and an account holding an administrative role are both refused. The settings topic says what each entry carries. Distribution lists and the directory A list that defines itself. Since 6.3.4, DistributionList.Membership is a string property, read and written like the moderator and bounce addresses, holding a membership query whose accounts are added to the list's own recipients. A domain administrator over COM may set domain, a pattern: in a domain it holds, or nothing; the server administrator may set any form, and a query the server cannot answer is refused when it is saved. Aliases, distribution lists and membership queries has the five forms and how a query is answered. The directory. Since 6.3.4 an account has Account.DirectoryTitle, .DirectoryDepartment, .DirectoryPhone, .DirectoryMobile, .DirectoryOffice, .DirectoryOptOut, .DirectoryNote, .DirectoryPhoto (base64) and .DirectoryPhotoType (read-only). The row is read at the first get and written by Account.Save() with the rest. A change of the opt-out through Account.Save() is recorded on the audit trail as directory-opt-out-changed. The Import Tool The Import Tool is a Windows program (Start menu > hMailServer > Import Tool) that works through the COM API: it brings mbox files into an account, handing each message to the server through Utilities.ImportMessageFromFileToIMAPFolder, reads a Maildir with its flags, and creates accounts in bulk from a text file. Bringing mailboxes in covers each of its jobs. The breach check applies to a COM script. It is off by default. Where it has been switched on, it runs whenever a password is set through COM Account.Password, the Import Tool's included, and a bulk operation makes one request per account: run a large import against a mirror, or with the check off. What it does not do The COM API and the Control Panel do not exist on Linux, and there is no Import Tool on Linux. The Import Tool does not read PST files. The file goes into the server's import directory, and REST, hmctl or the Control Deck start the job. COM has no API keys and cannot delete a role. These features have no COM surface at all and are REST only: SCIM provisioning, SAML and OpenID Connect sign-in, passkeys, the S/MIME authority, the send API, migrations, IP warm-up, eDiscovery, mail in to a webhook and mailbox encryption at rest, among others the wiki lists. Where to read more The COM API and Scripting, sections 23.1 to 23.5, and APIs Reference, sections 8 and 9. Roles.md, ChangedFromStock.md, DistributionLists.md and Directory.md. Migration.md for the Import Tool, and Upgrading.md for what an upgrader's scripts now see. The public documentation's COM API chapter and its Import Tool section. On this forum: Upgrading to 6.3.4, the 6.3.4 announcement, event scripts, and the REST API for what COM does not reach.
  • 0 Votes
    1 Posts
    135 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) refuses a rule's Run function action when the rule is saved unless it names a plain function, for a rule of every owner, over COM and REST, and setting one on a domain's or an account's rule is held to the server administrator alone, where up to 6.3.6 a credential confined to named domains, a domain administrator among them, could set it. An event script that goes on in a transaction after Database.ExecuteSQL has failed is refused every statement after it once the database has ended the transaction, and Database.CommitTransaction then fails: call Database.RollbackTransaction and run the whole transaction again, or stop. A ScriptTimeout above 4,294,967 seconds is taken as that, where it ended every Windows event script at once, and the VBScript calls of OnClientValidatePassword, OnExternalAccountDownload and OnDeliveryFailed are quoted as every other call is. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) adds a sixteenth event, OnIMAPMessageMoved, called once for each message an IMAP MOVE or UID MOVE moved between two folders of the account signed in, after the command and off the IMAP thread, with the account and thirteen values, in VBScript and JScript on Windows and JavaScript on Linux; it is off as shipped, and IMAPMessageMovedEvents (COM Settings.Scripting.IMAPMessageMovedEvents, imap_message_moved_events in PUT /api/v1/settings/scripting) switches it on (EventScripts.md). Work that outlasts a stop of the servers now writes its interruption to the debug log, not the ERROR log, so OnError is not called for it, 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. See the 6.3.8 announcement. This topic covers event scripts in hMailServer 6.3.4: the languages, switching scripting on, the fifteen events, the objects, the limits and errors, and the 6.3.4 security fix. If you run a Windows server older than 6.3.4 with event scripting on, that fix is the one thing to know first: a value written into a JScript handler's call, a password among them, could run code inside the server, and so could the name in a rule's Run function action, in either language. The section on CVE-2026-100741 below says what to do until the server is upgraded. What an event script is An event script is one file, EventHandlers.vbs or EventHandlers.js, in the server's Events directory, holding functions named after the events they handle. When scripting is on, the server calls the function for each event as it happens, hands it the objects the event is about (the client, the message, the account) and reads back what the function decided through the Result object. A rule's Run function action calls a function of the same file by name, and it too needs scripting on. Scripting is off as shipped, and with it off no handler is called. The file is loaded all the same: at start, at every reload, at Reload() and at POST /api/v1/settings/scripting/reload. Loading a file runs its code outside functions, once to compile it and once more for each of the fifteen handlers looked for, so keep the top level to declarations. A top level that throws, or runs past the timeout, is reported as a load that failed (HM5016, HM5710 or HM5021), whether scripting is on or not. A handler runs inside the server, on the thread that is doing the work, as the service's account: LocalSystem on a Windows installation that began before 6.3.4, NT SERVICE\hMailServer on a new one from 6.3.4, the hmailserver user on Linux. Everything behind a slow script waits, so try a script first on a server not carrying your mail. Which language runs where Language Windows Linux Engine File VBScript runs refused at load Windows Active Scripting (vbscript.dll) EventHandlers.vbs JScript runs refused at load Windows Active Scripting (jscript.dll), ECMAScript 3 EventHandlers.js JavaScript refused at load runs QuickJS 2025-04-26, built into the server, ES2023 EventHandlers.js Up to 6.3.3 a Linux server had no script engine and refused any script; since 6.3.4 it runs JavaScript in QuickJS, with the same fifteen events, function names and objects. Windows keeps VBScript and JScript unchanged. The language is one setting: Settings.Scripting.Language over COM (Windows only), "language" in PUT /api/v1/settings/scripting over REST, and the Language (VBScript, JScript or JavaScript) field on the Control Panel's Advanced page. Since 6.3.4 it accepts all three words on both platforms, in any case, and refuses any other word without storing anything. It is seeded VBScript on every platform, so a Linux server switching scripting on sets JavaScript first. Storing the language loads nothing. The script in force goes on running, in the language it was loaded in, until the next load. A language the build does not run is refused when the script is loaded: one line in the ERROR log, HM5016 at a start or HM5710 at a reload (the earlier script then stays in force), naming the language to use, once per load. While scripting is switched off this refusal is not reported, so switch scripting on and reload before reading the log for it. GET /api/v1/status says which languages a build runs, under build.script_languages: ["VBScript","JScript"] on Windows, ["JavaScript"] on Linux. Switching it on On Windows there are two Control Panel pages under Maintenance: Advanced, whose Scripting tab turns the engine on and chooses the language, and Event scripts, which edits the file. Save & reload writes a .bak first, then reloads and checks the syntax. Over COM the switch is Settings.Scripting.Enabled; Settings.Scripting.Reload() re-reads the file and CheckSyntax() returns the error text, where empty means clean. There is one file and no include mechanism. On Linux, from the command line: Set the language: hmctl settings set scripting --set-string language=JavaScript Write the file as the service's user: sudo -u hmailserver tee /var/lib/hmailserver/events/EventHandlers.js <<'EOF' function OnAcceptMessage(client, message) { if (message.Subject.includes('[URGENT]') && !client.Authenticated) { Result.Value = 2; Result.Message = 'Urgent mail comes from people who have logged on.'; } } EOF Switch scripting on: hmctl settings set scripting --set enabled=true Confirm which file the server reads: hmctl settings get scripting current_script_file Load it with POST /api/v1/settings/scripting/reload. POST /api/v1/settings/scripting/check answers {"result":""} when the file compiles and the error, with its line, when it does not. The fifteen events Every handler is optional. When the script is loaded the server checks which of these names the file declares and fires only those. Names are matched exactly: OnErrorLog is not a handler, the name is OnError. Handler Fires when Called with What Result.Value does OnClientConnect(client) any TCP connection is accepted, before a greeting the client 1 closes the connection at once OnHELO(client) the SMTP client sent HELO or EHLO the client 1 answers 554 5.7.1 Rejected; 2 answers 554 5.7.1 with Result.Message; 3 answers 453 4.7.0 with Result.Message OnClientLogon(client) an SMTP, POP3 or IMAP client finished a logon attempt, successful or not the client; client.Authenticated says how the logon went nothing: the logon has been decided OnClientValidatePassword(account, password) a password is being checked, before the built-in check the account and the password typed 0 lets the client in; 1 turns it away; 2 (as given) leaves it to the stored password OnSMTPData(client, message) the DATA command arrived, before the body the client and the message as OnHELO OnAcceptMessage(client, message) the body has been received and is about to be queued the client and the message as OnHELO OnRecipientUnknown(client, message) a RCPT TO named an address this server does not host the client and the message nothing OnTooManyInvalidCommands(client, message) an SMTP client sent too many invalid commands the client and the message nothing OnDeliveryStart(message) the first step of a delivery attempt, before virus scanning the message 1 deletes the message OnDeliverMessage(message) after virus scanning and global rules, before the recipient split the message 1 deletes the message OnDeliveryFailed(message, recipient, error) once per failed recipient; may fire several times per message the message, the recipient, the error text nothing OnExternalAccountDownload(fetchAccount, message, uid) a message was fetched from an external POP3 or IMAP account the fetch account, the message or null, its UID on the remote server 1 deletes it on the remote server now; 2 after Result.Parameter days; 3 never OnBackupCompleted() a backup finished nothing nothing OnBackupFailed(reason) a backup failed the reason nothing OnError(severity, code, source, description) anything is written to the ERROR log the error, as the ERROR log has it nothing OnError's severity is 1 Critical, 2 High, 3 Medium, 4 Low, and its code is the HM<nnnn> number without the prefix. OnClientValidatePassword setting 0 is a full authentication bypass for that account, on every protocol. A handler that bridges to another directory should fail closed: set 1 when the lookup errors, never 0. The objects a handler is given The objects an event has are also globals of the same names, HMAILSERVER_CLIENT, HMAILSERVER_MESSAGE, HMAILSERVER_ACCOUNT, HMAILSERVER_FETCHACCOUNT, Result and EventLog, and the parameter and the global are the same object. An object an event does not have is null as a parameter; as a global it may not be defined at all (the message of an external download that failed), and reading an undefined global throws a ReferenceError, so a handler reads its parameter. Do not use Result in the seven events that are notifications. EventScripts.md says it is not defined at all in OnClientLogon, OnRecipientUnknown, OnTooManyInvalidCommands, OnDeliveryFailed and OnError; the public documentation adds that the server makes no Result for OnBackupCompleted and OnBackupFailed. Object Members On JavaScript (Linux) Client Port, IPAddress, Username, HELO, Authenticated, EncryptedConnection, CipherVersion, CipherName, CipherBits, SessionID the same, all read-only Message ID, Filename, Subject, From, FromAddress, To, CC, Date, Body, HTMLBody, Charset, Recipients, AddRecipient(name, address), ClearRecipients(), HeaderValue(name), HasBodyType(type), Flag(flag), Size (KB), State, DeliveryAttempt, UID, InternalDate, Copy(folderId), RefreshContent(), Save(), Attachments, Headers Attachments and Headers are not there; SetHeaderValue(name, value) and SetFlag(flag, on) write; InternalDate is text Result Value, Message, Parameter Value and Parameter take a whole Number only EventLog Write(text) the same FetchAccount the COM FetchAccount readable, not assignable; Password, Save(), DownloadNow() and Delete() are not there Account the COM Account, freshly loaded the properties that describe the account are readable, not assignable; Password, the collections and every method are not there Save() is what writes a change to disk. Changes to the subject, the body and the headers are written to the message file by Save(), on both platforms; a handler that does not call it changes nothing on disk. Do not call it in OnSMTPData: the public documentation says the message is still being processed there and saving corrupts it, and that only the character set, the file name, the envelope sender and the recipients are reliable at that point. Change a message in OnAcceptMessage. Result is read after the handler returns. On JavaScript a string ('1' too), a boolean, null, undefined, NaN, a fraction or an object assigned to Value or Parameter throws a TypeError, and the event goes on with the Result it had. So in OnClientValidatePassword, Result.Value = decisions[user] for a user the table does not have leaves 2, the stored password. On Windows, COM turns a boolean, undefined, a fraction and a string of digits into numbers, and undefined into 0: the same line in JScript lets that client in. EventLog.Write is the debugger. It appends a line to hmailserver_events.log in the log directory, and there is no breakpoint. A rule's Run function A rule's Run script function action calls the named function as <function>(HMAILSERVER_MESSAGE), then reloads the message from disk, so anything the function saves is seen by the rest of the rule set. Since 6.3.4 it works on Linux, where the function is a JavaScript one in EventHandlers.js. Since 6.3.4 the action names the function alone, as a plain identifier: ASCII letters, digits, _ and $, not starting with a digit. StampReceivedDate qualifies; StampReceivedDate(x) does not. On Windows, in VBScript or JScript, any other name is refused as HM5018 each time the rule matches and the function is not run. On Linux such a name is never evaluated and finds no function: nothing is run, and the ERROR log has a Script Error line, not HM5018. A ported handler JScript and JavaScript share the file name, so a JScript file carried from Windows to Linux is the file the JavaScript engine is given. EventScripts.md's example starts from VBScript on Windows: Sub OnAcceptMessage(oClient, oMessage) If InStr(oMessage.HeaderValue("X-Spam-Flag"), "YES") > 0 And Not oClient.Authenticated Then oMessage.HeaderValue("X-Quarantine") = "yes" oMessage.Subject = "[SPAM] " & oMessage.Subject oMessage.Save EventLog.Write("Tagged spam from " & oClient.IPAddress) End If End Sub The same handler for a Linux server, in EventHandlers.js: function OnAcceptMessage(client, message) { if (message.HeaderValue('X-Spam-Flag').includes('YES') && !client.Authenticated) { message.SetHeaderValue('X-Quarantine', 'yes'); message.Subject = `[SPAM] ${message.Subject}`; message.Save(); EventLog.Write(`Tagged spam from ${client.IPAddress}`); } } What changes in a port: Parameterised properties have no assignment. message.HeaderValue('X') = 'v' becomes message.SetHeaderValue('X', 'v'), and message.Flag(n) = true becomes message.SetFlag(n, true). The reading forms are unchanged. Names are case-sensitive and spelled as the IDL spells them. JScript found message.FileName; JavaScript needs message.Filename. No CreateObject, no new ActiveXObject. Both exist on JavaScript only to throw a TypeError that says so. null, not Nothing, for an object an event does not have. The limits A fresh engine for every event, on Windows and Linux alike: the whole file is parsed and run, the handler is called, and the engine is freed before the event goes on, so nothing a script makes outlives the event. No file system, no network, no processes on JavaScript. QuickJS keeps these in quickjs-libc.c, and that file is not in the tree. No module loader is installed, so import() is refused and an import statement is a syntax error; there is no print and no console. What a handler can change is what its objects change: a message's file through Save(), a copy into a folder through Copy(), the event log through EventLog.Write. On Windows, ScriptAllowedObjects decides what a script may create with CreateObject or new ActiveXObject. Its shipped value is *, every class. Empty means no class beyond the server's own objects, and a comma-separated list of ProgIDs means only those. Up to 6.3.3 it applied at a service restart; since 6.3.4 a save applies it. It has nothing to apply to on Linux. ScriptTimeout is in seconds, with 0 for no limit. It is kept in the settings store: set it with PUT /api/v1/settings/ini/ScriptTimeout, COM Settings.SetIniSetting or the Control Panel, not by editing hMailServer.ini. Limit Platform Value When a handler reaches it ScriptTimeout Windows 60 seconds by default HM5019; the interrupt cannot release a handler blocked inside a COM call ScriptTimeout Linux 60 seconds by default HM7341; the event goes on with whatever Result the handler had set Memory Linux 64 MB for each run, a constant HM7342; the event goes on Stack Linux 512 KB of the calling thread's stack a runaway recursion throws an InternalError The Linux timeout does not stop one call into a built-in that loops in the engine's C code without polling, such as includes or indexOf on an object whose length is huge: do not call an array method on an object whose length the handler has not checked. Errors, and where they are written What Where A script that does not compile, or whose top level throws HM5016 at start (nothing loaded), HM5710 at a reload (the earlier script stays in force) A top level that runs past the timeout HM5021, and the load refused An exception a handler does not catch one Script Error line in the ERROR log; the event goes on The load is all or nothing on every engine, so a typo in a reload never switches a working OnClientValidatePassword off. The 6.3.4 security fix: CVE-2026-100741 Up to 6.3.3 a password could run code inside the server. On Windows, with event scripting on, the language JScript and an OnClientValidatePassword handler in EventHandlers.js, any client that could reach a sign-in and name an existing, active account could have JScript of its own run inside hMailServer.exe, with the rights of the service's account, before it had authenticated. The server built each call as source text and escaped the apostrophe but not the backslash before it. With ScriptAllowedObjects at its shipped *, that script could create any COM object, WScript.Shell among them. Path Who could use it Open in The password given to OnClientValidatePassword any client that can reach a sign-in JScript The UID given to OnExternalAccountDownload a remote POP3 server JScript The error text and failed recipient given to OnDeliveryFailed a remote mail server JScript The name in a rule's Run function action whoever may edit a rule, a domain's own administrator included JScript and VBScript Which servers are affected. The flaw is CVE-2026-100741; the record was not yet public when this was written. The flaw is in every release from 6.0.0 to 6.3.3: the escaping was checked at every one of those tags (GitLab issue 27). Treat any server older than 6.3.4 with event scripting on and JScript as its language as affected. A rule's Run function action wrote its function name into the call unescaped, in either language, so VBScript servers too. What 6.3.4 changes. Every JScript value is escaped in one helper, ScriptServer::EscapeForJScriptLiteral: every backslash first, then both quotes, CR, LF, U+2028, U+2029 and NUL; OnBackupFailed's reason, which neither language escaped and the table above does not list, is escaped in both. A rule's Run function name must be a plain identifier. On Linux the handler is called with values as values, so nothing is quoted into source text. A JScript handler now receives every value exactly as it was, backslashes included; one that undid the old half-escaping by hand must stop doing so. Until you can upgrade, switch event scripting off. That is the only measure that closes every path, in either language. A server that cannot do without its scripts can narrow what is open: Take the OnClientValidatePassword, OnExternalAccountDownload and OnDeliveryFailed handlers out of a JScript EventHandlers.js and reload the scripts. Check the reload: one that fails (HM5710 in the ERROR log) keeps the earlier script in force, those handlers included. In either language, let nobody but the server administrator make or edit a rule with a Run function action. Roles.md lets an account's rules be written over COM by the server administrator, an administrator of its domain, or the account itself, so "whoever may edit a rule" is wider than administrators. Narrow ScriptAllowedObjects from *, which limits what an injected script can create. The key exists from 6.2.25, and before 6.3.4 it applies only after a service restart. Moving a handler to VBScript closes the password, UID and error-text paths and leaves the rule path open. Before you upgrade, check your rules' Run function actions and put the function's bare name in any that does not qualify. After it, an HM5018 in the ERROR log (on Linux, a Script Error line naming the function) means one was missed. Upgrading to 6.3.4 covers the upgrade itself. Reporting a vulnerability goes through a confidential GitLab issue or the Service Desk address contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, and not through a public issue or this forum. What it does not do OnClientValidatePassword is not consulted for SCRAM, which has no password to give it. Where to read more EventScripts.md: the member-by-member contract of every object, and the engine. The COM API and Scripting, sections 23.6 to 23.10. Rules and Sieve for the Run script function action, and Security Hardening, section 17.11. The public documentation's event scripts chapter. On this forum: hMailServer on Linux: what you get and what you do not, the 6.3.4 security announcement, the COM API on Windows and rules and the Run function action.
  • 0 Votes
    1 Posts
    125 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) renames two Control Panel pages this topic names: SSL certificates is TLS certificates and SSL/TLS is TLS versions & ciphers; Ctrl+K takes every old title and route to its page. See the 6.3.7 announcement. On Windows, hMailServer 6.3.8 (2 November 2026) reads each certificate and private key file, dh2048.pem and a port's client-certificate CA file from the file configured, and refuses with HM7536 a name it cannot hand over as it is named (work item 115): a port whose certificate or key such a name gives does not start, a client-certificate CA file verifies no certificate, and dh2048.pem is left out as a missing one is. Before upgrading, give each such file whose path holds characters outside ASCII, and the folders above it, an ASCII name; the Control Panel shows a certificate or key file whose name is refused as Name refused. On Linux every name goes over in UTF-8, so on a server that ran under a LANG that is not UTF-8, rename such a file to its UTF-8 name before upgrading. See the 6.3.8 announcement. This topic is the manual half of TLS on the SMTP, IMAP and POP3 listeners: your own certificate and where the Windows service may read it, then versions, ciphers, key exchange, session resumption, client certificates with SASL EXTERNAL, and certificates for IP addresses. Know this first: a port set to use TLS whose certificate cannot be loaded does not fall back to plain text. It does not start. The application log says HM5113, and the only other symptom is a listener missing. Where each thing is set, and when it applies What Set in Applies Certificates, and which port uses which Control Panel: TLS & certificates → SSL certificates and Connections & protocols → TCP/IP ports; the Deck's certificates and listeners views; /api/v1/certificates and /api/v1/ports; hmctl's certificate and port resources; COM At a reinitialise, or a service restart TLS 1.0 to 1.3, the cipher list for TLS 1.2 and below, the two cipher-order switches Control Panel: TLS & certificates → SSL/TLS; PUT /api/v1/settings; hmctl settings set; COM Settings See the note below TlsCipherSuites13, TlsKeyExchangeGroups and the four session settings The same page; PUT /api/v1/settings/ini/{name}; hmctl ini set; on Linux hmailserver --set-setting At a reinitialise A reinitialise is Apply now on the Control Panel's pending bar or in the Deck's Settings view, POST /api/v1/server/reinitialize, or Application.Reinitialize over COM. Every service stops and starts again in the same process; it ends the Deck's session and any drain in progress. GET /api/v1/server/settings/pending lists the [Settings] keys saved and not yet running, such as TlsKeyExchangeGroups; a port, a certificate, the versions and the cipher list are never in it. The Control Panel and COM are Windows-only; on Linux use the Deck, REST or hmctl. Do not edit hMailServer.ini. Since 6.3.4 the database holds the [Settings] keys, so a line changed in the file is put back (HM5804) or left unused; Settings: when a save takes effect explains the store and the reinitialise. When the versions and the cipher list apply. They and the two order switches are database settings (hm_settings rows), not [Settings] keys, so SettingsApply.md gives them no mark. Roadmap.md says they are applied in the call that builds each listener's TLS context, and SettingsApply.md says those contexts are built when the servers start; the wiki's Control Panel Pages page, written against 6.2.28, says the SSL/TLS page's COM settings apply at once. Press Apply now after changing any of them, and check from outside. Adding your own certificate Two PEM files. The certificate file must hold the leaf and the issuer chain: the server loads it twice, once as the certificate and once as the chain, and reports each failure separately. The private key is the second file. A passphrase-protected key is supported, per certificate. Give it in the add dialog, on the SSL certificates page's Private key passphrase card, in the Deck, or as private_key_password in POST /api/v1/certificates. It is write-only and kept under the machine's own secret store. The built-in backup and restore carries it to a new machine; a copied or shared database does not, so on a warm standby enter it again after a failover, and again on the primary after failing back, or the key does not load there and the TLS ports that use it do not start. An encrypted key with no passphrase is HM6170, and its port does not start. Where the files go. On Windows, a new 6.3.4 installation runs the service as NT SERVICE\hMailServer, which reads only what it has been granted (next section); put the files in Data\Certificates (C:\Program Files\hMailServer\Data\Certificates on a default installation), where a new 6.3.4 installation's self-signed pair is. An upgraded Windows server keeps the account it had; LocalSystem can read a file anywhere. On Linux the service runs as User=hmailserver under a unit with ProtectHome=yes, so nothing under /home is visible to it. The self-signed pair of a new installation is in /var/lib/hmailserver/Certificates, its key mode 0600 owned by the service's user; keep yours beside it, readable by that user. Copy the certificate file and the key into that folder. Add the certificate: on Windows, TLS & certificates → SSL certificates → Add certificate (a name, the PEM certificate file, the PEM key file, an optional passphrase); or the Deck's certificates view; or POST /api/v1/certificates with name, certificate_file and private_key_file. A file that is not there is refused (400). The Control Panel's grid then shows whether the certificate parses, when it expires, and whether the key is encrypted and matches. On Windows under the virtual account, run Grant access (next section), so it reads the new paths. Set the certificate on every TLS port: TCP/IP ports → Edit, the certificate list. Over REST, PUT /api/v1/ports/{id} is the whole record and fields left out take their defaults, so send protocol, address, port, connection security, certificate_id and the two client-certificate fields every time. Reinitialise. Check the application log for the codes below and, on Windows, Monitoring & troubleshooting → Server status for the listeners actually running and TLS & certificates → Transport encryption overview, which judges ports, certificates, versions and ciphers together. From a shell over the REST listener: add the certificate, then, once the ports are set, reinitialise and read the banner over implicit TLS. --from-json with - reads the body from standard input, which keeps a passphrase off the process list; the JSON holds the fields of step 2 and, for an encrypted key, private_key_password. hmctl certificate create --from-json - < certificate.json hmctl api POST /api/v1/server/reinitialize openssl s_client -connect mail.example.com:465 -quiet Code Message Meaning HM5113 Failed to load certificate file / …certificate chain… / …private key file The path, the format or the passphrase is wrong, the file cannot be read, or the key does not match. The port does not start HM5113 Error initializing SSL. Certificate not set. A TLS port has no certificate. The port does not start HM6170 — An encrypted key with no passphrase stored. The port does not start HM5991 The TLS certificate … expired on … / … is not valid until … Reported at startup; the port starts and offers it anyway, and clients refuse it. A future date usually means the server's clock is wrong HM5603 Unable to enable Diffie-Hellman key agreement dh2048.pem is missing: from Bin on Windows, from beside the binary in /usr/bin on Linux Replacing it later. When ACME renews, it logs ACME: Restarting servers to load the new certificate.; after replacing a file by hand, reinitialise likewise. If you publish DANE records, publish the new TLSA record before you rotate the certificate, never after; GET /api/v1/tlsa gives one 3 1 1 record per configured certificate, for _25._tcp only. Since 6.3.4 the alert certificate.expiring is on as shipped (7 days ahead), but nothing is mailed until AlertRecipient and AlertSenderAddress are set; the metrics listener exports hmailserver_tls_certificate_expiry_seconds. Since 6.3.4 a COM script's change to an SSL certificate is not in effect until Save(), and one never saved changes nothing, so a script that relied on an unsaved change must now save; the listeners still load the certificate at a reinitialise. ACME alongside. The built-in client gives its certificate to TLS ports that have none or hold the installation's self-signed one, and leaves alone a port that names a certificate somebody chose. The HTTPS listeners take files, not a certificate record: RestApiCertificateFile, WebServicesCertificateFile and MetricsServerCertificateFile, each with its private-key partner, all waiting for a reinitialise. ManageSieve reuses the certificate configured on an IMAP, POP3 or SMTP port. The service account's folder rules (Windows, since 6.3.4) hMailServer.exe /GrantServiceAccess, run elevated, grants NT SERVICE\hMailServer Modify on the program folder's own Data, Logs, Temp and Database, Read on its other folders, and what an administrator approved. It is the Grant access… button on Maintenance → Server limits & expert settings, card Windows service account, or, from an elevated Command Prompt in Bin: hMailServer.exe --confirm /GrantServiceAccess It checks every certificate and key file the configuration names (the SSL certificates, DKIM keys, and the REST, metrics and web-services files) against those grants. A file in a folder they reach needs nothing; where the file's own permissions keep the folder's grant from it, the grant gives Read on that file alone. A file they do not reach is listed OUTSIDE and is never given Read where it is, because the configuration could name any file on the machine. Move it into Data\Certificates, or approve its folder for Read with --approve-read "<folder>" or Yes at the question --confirm asks; approve only a folder you recognise. Some folders are refused even when approved, a share and a drive's root among them. ServiceAccount.md does not list the client-certificate CA bundle among the files the grant checks, so keep that bundle in Data\Certificates too. The account's rules in full, from what is never granted to the exit codes, are in Installing on Windows. Protocol versions and the legacy switches Version Bit in SslVersions COM REST, in PUT /api/v1/settings TLS 1.0 (legacy) 2 TlsVersion10Enabled tls_version_10_enabled TLS 1.1 (legacy) 4 TlsVersion11Enabled tls_version_11_enabled TLS 1.2 8 TlsVersion12Enabled tls_version_12_enabled TLS 1.3 16 TlsVersion13Enabled tls_version_13_enabled The shipped value is 24, TLS 1.2 and 1.3 only; SSLv2 and SSLv3 cannot be switched on at all. Do not re-enable TLS 1.0 or 1.1 unless a specific old client forces you to, and plan to replace that client. The versions and the cipher list also govern the connections this server makes when it delivers. The REST, web-services HTTPS and metrics listeners add a TLS 1.2 minimum of their own, so TLS 1.0 switched on for an old mail client stays off for the management API, and a server left with only TLS 1.0 and 1.1 leaves those three listeners with no version at all. Every version switched off is HM5990 and falls back to TLS 1.2 and 1.3; turning TLS off is done per port. Cipher suites: SslCipherList, AEAD-ONLY and TlsCipherSuites13 SslCipherList is an OpenSSL cipher string for TLS 1.2 and below only; it does not touch TLS 1.3. The shipped value is the long-standing list, which also admits CBC-mode suites and static-RSA key exchange. A string OpenSSL rejects is HM5511, and the previous list stays. AEAD-ONLY is a named preset, not an OpenSSL string: set SslCipherList to that word and the server applies a vetted list of forward-secret AES-GCM and ChaCha20-Poly1305 suites (ECDHE and DHE), excluding every CBC-mode suite and static-RSA key exchange. The cost: TLS 1.2 clients that speak only CBC cannot connect, and TLS 1.0 and 1.1 are left with no usable suite, so never enable them alongside it; the Transport encryption overview flags that combination. A misspelled preset goes to OpenSSL, which rejects it, and HM5511 names the string. TlsCipherSuites13 takes RFC 8446 names, colon separated, for example TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256. Empty, the shipped value, leaves OpenSSL's defaults. A list rejected outright is HM6120 and the defaults stand; names this build does not know are named in HM6121 and the rest of the list is applied. Prefer server cipher order and Prioritize ChaCha20-Poly1305 (TlsOptionPreferServerCiphersEnabled, TlsOptionPrioritizeChaChaEnabled; over REST tls_prefer_server_ciphers, tls_prioritize_chacha) are both off as shipped. The second works only with the first on and TLS 1.2 or 1.3 enabled. A compliance baseline from a shell, then a reinitialise: hmctl settings set --set ssl_cipher_list=AEAD-ONLY --set tls_prefer_server_ciphers=true hmctl ini set TlsCipherSuites13 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 hmctl api POST /api/v1/server/reinitialize The ini line takes the administrator password (--password-file or HMAIL_PASSWORD); an API key is refused there with 401. Key exchange: the hybrid post-quantum groups TlsKeyExchangeGroups ships as X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:secp256r1: two ML-KEM hybrids first, then the classical curves, so a modern peer agrees a hybrid and an older one falls back. The list reaches every TLS context in the server: the mail listeners, ManageSieve, the three HTTPS listeners and the outbound client, so mail this server sends uses it too. A list OpenSSL rejects, an empty one, or one that leaves no group, is HM5720 and falls back to secp384r1:x25519:secp256r1, without the two hybrids; if even that is refused, HM5721 and OpenSSL's own defaults. Session caches, tickets and ticket-key rotation All four are on the SSL/TLS page's Session resumption tab and wait for a reinitialise. At their defaults three of them make no OpenSSL call at all. Setting Default A non-default value TlsSessionTicketsEnabled on Off: no tickets on any version. TLS 1.2 clients fall back to the in-process cache; TLS 1.3 clients get no ticket TlsSessionCacheSize 0, OpenSSL's cap of 20480 A positive value replaces the cap; -1 turns the server-side cache off, leaving tickets alone TlsSessionTimeoutSeconds 0, OpenSSL's 300 Bounds how long a leaked resumption secret stays useful TlsTicketKeyRotationSeconds 0, one key for the life of the process A captured ticket can be decrypted for at most two intervals; 86400 is a day Rotation is the one worth turning on for forward secrecy. Without it every ticket is sealed under one key made at startup, and that key, recovered later from a memory dump or a hibernation file, opens every ticket ever recorded. Rotation is process-wide. Each listener keeps its own session-ID context, so a session made on a port that asks for no client certificate is never resumed on one that requires it. Client certificates (mutual TLS) and SASL EXTERNAL The policy is per port, on the TCP/IP port dialog (COM ClientCertificatePolicy and ClientCertificateCAFile; REST client_certificate_policy and client_certificate_ca_file). Policy Behaviour CA bundle cannot be loaded Off (0) Nothing is asked. The default — Request (1) Asked, verified if offered, the outcome logged; the handshake never fails over it HM6140, and the port starts anyway Require (2) The handshake fails unless the client presents a certificate chaining to the port's CA bundle The listener is not started The bundle is the only trust anchor for that port, not the Windows certificate store. A policy on a port without TLS is refused when you save it; Roadmap.md adds a policy with no CA file, and Require on a STARTTLS (optional) port, where a client that never issues STARTTLS is never asked. A hand-edited database that has one anyway is HM6142 (require stops the listener, request only logs). Start with Request to see which clients could survive Require; Require on the port ordinary mail clients use would lock out every one without a certificate. EXTERNAL. A verified client certificate that names an address makes AUTH EXTERNAL (RFC 4422) available on that connection, on SMTP, IMAP and POP3, and to nobody else. It signs in as the mailbox the certificate names: a subjectAltName rfc822Name, the subject's emailAddress, or an address-shaped common name; a certificate for one user is never a credential for another. Since 6.3.4 such a sign-in also meets the domain's and the account's country policy, and an SMTP session signed in with it is no longer refused its second message with 550. Since 6.3.4 a mailbox encrypted at rest refuses EXTERNAL on IMAP and POP3, opening with its own password only. A certificate for an IP address (Linux, since 6.3.4) This is about certificates this server checks when it connects out, not the one it serves. An endpoint given by its address (an object store, a webhook, the update feed, an SMTP route, the syslog sink) presents a certificate with the address in an iPAddress entry, where RFC 5280 puts it. The Linux check read only dNSName entries and the common name, so such a certificate was refused; it is accepted now, and the HTTPS client no longer sends an address as the server name (SNI), as the IMAP migration client already did not. Newly refused on Linux: a wildcard taken to cover an address, and the common name of a certificate whose iPAddress entries name other addresses. Windows always accepted such certificates. What it does not do It does not obtain or renew certificates: that is ACME, in Let's Encrypt certificates are automatic. Connection security per port, and what a mail client is told: Connecting a mail client. MTA-STS, DANE, TLS-RPT and remote domain policies: Requiring TLS. The hardening checklist: Hardening a public mail server. TLS to the database is [Database] TlsMode and TlsCaFile, with no Control Panel or Deck editor: Choosing a database and encrypting the link. Where to read more Wiki: Encryption and Certificates §9.2 to §9.4 and §9.12, Ports Reference §3.4, Settings Reference, Security Hardening §17.3. Documents: ServiceAccount.md, SettingsApply.md, SecureDefaults.md, SecretsAtRest.md, DatabaseTls.md, AlertsAndAuditTrail.md. On the forum, for the certificate-expiry alert and where it is mailed: Alerts, the audit trail, syslog and the journal. The documentation page, 9.2 The manual way.
  • 0 Votes
    1 Posts
    126 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) bears on relocating a Windows installation to another drive. hMailServer.exe /ProtectFolders, which setup runs at every installation and upgrade, now hardens the program folder, Bin and ControlPanel: where hMailServer was installed 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 folder under Program Files was never affected. The procedure below copies the tree and re-registers it without running setup, and a folder below a drive's root takes the permissions the root hands down, so an installation relocated there is hardened by the upgrade to 6.3.7, or on 6.3.7 by running hMailServer.exe /ProtectFolders as an administrator, not by the move (on 6.3.6 or earlier follow the icacls steps in ServiceAccount.md, since those versions' /ProtectFolders leaves the three program folders as they are); before upgrading an installation at a drive's root, follow the steps "Before upgrading an installation at a drive's root" in ServiceAccount.md. Where accounts that are not administrators could sign in to the computer while the installation was in such a folder, the release says to reinstall the programs into a new folder that only the administrators may change, not over the installation, with setup given the new folder by /DIR=: over an installation setup installs into the folder it finds, and a 32-bit installation's uninstall entry overrides /DIR= until it is taken away, which the "re-run the installer" repair below meets. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) has setup make or harden the program folder, Bin and ControlPanel before it copies a file or runs a program of the installation. One of them that another account could change, as a folder below a drive's root can be, setup hardens and then refuses, with exit code 7, until Windows has restarted; and 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, it refuses with exit code 7, naming the icacls that deals with it and a restart of Windows before setup runs again. On Windows 6.3.8 may also refuse, with HM7534 or HM7536, TlsCaFile, PostgreSQLSslRootCert, a TLS certificate or private key file, OAuth2PublicKeyFile or AcmeCertificateDirectory where its path holds characters outside ASCII, so give the new root, and the folders above it, an ASCII name. See the 6.3.8 announcement. This guide covers two different moves. The first is relocating a configured installation to another directory or drive on the same machine, for example from C:\Program Files (x86)\hMailServer to C:\Program Files\hMailServer. The second is renaming a mailbox or moving it to another domain, which is an operation of its own since 6.3.4. Moving to new hardware is a third thing, and Moving to new hardware, and why Windows to Linux is a rebuild covers it. The one thing to know first: relocating is not one change. The installation path is recorded in the registry, in the service, in the COM registration, in hMailServer.ini and in a handful of database columns, and since 6.3.4 a [Settings] path edited in the file is ignored in favour of the value stored in the database. The relocation runbook is written in Windows terms: the registry, the service and the COM registration. The documents used here give no separate procedure for a Linux installation. Installing somewhere new is a different, solved problem: /DIR="<path>" on the installer puts a fresh installation wherever you want. The good news first The mail store itself is already relocatable. Message rows hold a bare {guid}.eml with no directory, and the path is rebuilt at every read against the data directory. Moving the data directory and pointing DataFolder at its new home moves every message with it. The exception is a database that predates hMailServer 5.4, where the column can hold a full path. Before you touch anything Take a backup and verify it restores (Backup and restore). Everything below is reversible only while the old tree and a good backup both exist. Ask the server whether its message rows are relative. In the Control Panel, Monitoring & troubleshooting → Diagnostics → Test message file locations. If it reports ERROR: Full paths are stored in the database, the move is still possible, but converting the paths becomes mandatory rather than optional. Write down what you are about to strand. The same Diagnostics page has an Installation paths section that lists the registry root, every [Directories] key and every path-shaped setting the running server holds, with the ones that do not exist marked. It changes nothing. The inventory Where What Repaired by Bin\hMailServer.ini, [Directories] ProgramFolder, DataFolder, DatabaseFolder, LogFolder, TempFolder, EventFolder Editing the file Bin\hMailServer.ini, [Database] PostgreSQLSslRootCert and TlsCaFile Editing the file. Leave the connection keys alone: they name a server, not a path The settings store (hm_inisettings) ArchiveDir, AcmeCertificateDirectory, OAuth2PublicKeyFile, UpdateTrustRootsFile, UpdateLogPublicKeyFile, and the certificate and private key files of the REST API, metrics and web services listeners Storing the new value, not editing the file (since 6.3.4) Registry, HKLM\SOFTWARE\hMailServer in the 32-bit view InstallLocation By hand Registry: the service's ImagePath, LocalServer32 (79 keys), InprocServer32 (15), the TypeLib values hMailServer.exe /Register Registry, HKLM\...\Uninstall\hMailServer_is1 InstallLocation, UninstallString, DisplayIcon Nothing; re-run the installer The database Certificate and key files, DKIM key files, the client-certificate CA bundle, backupdestination, the ClamWin and custom scanner paths, rule actions Each on its own page What breaks when a key is left behind. With a stale DataFolder the store appears empty. With a stale LogFolder logs are written to the old tree, silently. With a stale EventFolder event scripts are not found and events stop running. The 32-bit view is not optional. The server reads InstallLocation through KEY_WOW64_32KEY, and the installer writes it there. Writing the 64-bit view changes nothing and looks like it worked. The procedure Stop the service, and confirm it is stopped. Nothing here may run against a live server. Copy the tree. Copy, do not move: the old tree is the rollback. Include Bin\hMailServerApiKeys.ini if it exists, since the REST API key store lives there and no installer manifest mentions it. Grant the service account access to the new tree as 6.3.8's RelocatingAnInstallation.md does: a copy takes the permissions of the folder it lands in (under Program Files every user's Read on the new Data, Logs, Temp, Database, Events and hMailServer.INI) and not the old tree's entries for NT SERVICE\hMailServer, so the document makes the new root with the permissions setup gives a program folder before the copy, runs hMailServer.exe /ProtectFolders from the new Bin after step 5's /Register, and grants a service that runs as anything but LocalSystem its folders again with /GrantServiceAccess. A server that cannot write its log directory starts and then says nothing. Rewrite the file keys in <new root>\Bin\hMailServer.ini whose value was under the old root: the [Directories] folders, and [Database] PostgreSQLSslRootCert and TlsCaFile, the second named beside the first by 6.3.8's document, since with TlsMode=verify-full a CA file left in the old tree stops the server reaching its database once that tree is deleted (HM7531). Nothing else, and not a folder that was outside the old tree and has not moved: a DataFolder pointed at an empty directory makes the store appear empty. Repoint the registry root, from an elevated prompt: reg add "HKLM\SOFTWARE\hMailServer" /v InstallLocation /t REG_SZ /d "<new root>" /reg:32 /f. /reg:32 is the whole of the point. Re-register the server, from an elevated prompt, from the new Bin: hMailServer.exe /Register. It registers the path of the executable that runs it. Start it, and check what it read. Read the application log, and since 6.3.4 the ERROR log too. Confirm that the Diagnostics page's Installation paths shows the new paths (6.3.8's document names it; the Control Panel has no Settings → Directories page), that your domains are listed, and that the log file you are reading is under the new log directory. Convert full message paths, only if the diagnostic found any: run the Data Directory Synchronizer, in its import mode, on the old installation before step 2 copies anything, as step 0 of 6.3.8's RelocatingAnInstallation.md has it, not against the new data directory after the move: it converts only a row whose full path is where the file is found, so after the move it would import every such message a second time, and its other mode would delete the copies. A hand-written UPDATE hm_messages SET ... is not a supported converter. Store the [Settings] paths and fix the database-borne paths (both below). Verify before deleting anything: send and receive a message; complete a TLS handshake on every port that has a certificate; run a backup to completion; run an event script, if you use them; check the log directory is being written. Keep the old tree, unreachable by the service, for at least one full backup cycle. Do not make it unreachable until the [Settings] paths are stored and a reinitialise has picked up the new certificate and ACME paths. The failure this prevents. A server whose anchors are stale reports Running and serves an empty configuration. That failure is silent. The [Settings] paths: 6.3.3 and earlier, and since 6.3.4 On 6.3.3 and earlier these are [Settings] keys of hMailServer.ini like any other, and editing the file moves them. Since 6.3.4 it does not: the stored value is used, the edit is named in the error log as HM5804, and, where the service can write the file, the line is put back to the old path. RelocatingAnInstallation.md and the wiki page between them give three ways to store the new values. The first is the document's own, and the one to use: When How Where it is described After the moved server is running In the Control Panel under Settings, with Settings.SetIniSetting over COM, PUT /api/v1/settings/ini/{name}, or hmctl ini set <name> <value> RelocatingAnInstallation.md, step 9, and the wiki Before stopping the old server The same four ways, as the last thing before the service is stopped. A save applies ArchiveDir, OAuth2PublicKeyFile, UpdateTrustRootsFile and UpdateLogPublicKeyFile at once, not at the next start as the wiki page has it, so the running old server turns to a tree not yet copied: archiving stops, and token validation and update verification refuse. The database then holds the new paths, so going back to the old tree means storing the old values again The wiki, corrected by SettingsApply.md At the first start, if the service is already stopped A [SettingsOverride] section in the new hMailServer.ini, named in the error log as HM5807; then store the same values and remove the section before the next restart The wiki A save applies every one of these paths at once except the listeners' certificate paths and the ACME certificate directory, which are bound when the servers start and wait for a reinitialise (Apply now, or POST /api/v1/server/reinitialize). Until then the server runs on the old paths, which still resolve because the tree was copied rather than moved. PasswordPepper is the exception to all of this: it lives in the file only, is not a path, and comes across with the copied file. The database-borne paths SSL certificates. Since 6.3.7 the Control Panel's SSL certificates page has Edit…, which changes a certificate's name and files and keeps its id, so every port that presents it keeps it and reads the new files at the next reinitialise; deleting a certificate and adding it again, as the document had it until 6.3.8, leaves each port that presented it with none, and such a port does not start (HM5113). It can also be set over COM. DKIM keys. Domain → DKIM, the primary and, if used, the secondary. A domain whose key rotation manages its keys refuses that edit. Client-certificate CA bundle, per port. A port set to require a client certificate refuses every client when its bundle cannot be loaded. Archive index. hm_archiveindex.archivepath is matched on the exact string by retention, legal hold and the address eraser. Do not bulk-rewrite it. If the archive directory moved, move the archive back, or accept that the rows written before the move point at the old location; there is no supported converter for this column. What stays broken, and why Add/Remove Programs still points at the old unins000.exe. Do not edit unins000.dat. The supported repair is to re-run the installer at the new root. Start-menu shortcuts hold absolute targets and nothing repairs them. Message content. The old path appears inside messages the server itself wrote, such as a delivery report. Never rewrite message content. Renaming a mailbox, or moving it to another domain New in 6.3.4 (schema 6056, whose table hm_mailboxmoves is the move's journal). new_address carries the domain, so renaming and moving are the same operation: [email protected] to [email protected] is a rename, [email protected] to [email protected] is a move. A move keeps the account's row, so everything keyed on the account id follows without being touched: rules, folders with their subscriptions and access-control entries, application passwords, the second factor, contacts, calendars and the role assignment. Copying the mail into a second account loses all of it. Interface How Control Deck The Move or rename card in the account editor hmctl hmctl mailbox status [email protected], then hmctl mailbox move [email protected] [email protected] REST POST /api/v1/accounts/ann%40example.com/move with {"new_address": "[email protected]", "keep_alias": true}; GET on the same path says whether anything holds the mailbox PowerShell Move-HmMailbox -Address [email protected] -NewAddress [email protected] Control Panel (Windows) Move or rename… on the account's General tab COM (Windows) Account.MoveTo(NewAddress, KeepOldAddressAsAlias) or Utilities.MoveMailbox(Address, NewAddress, KeepOldAddressAsAlias) The old address is kept as an alias by default, pointing at the new address. Turn it off (keep_alias false, --drop-alias) only when the address is to stop working: mail to it is then refused at RCPT TO. What moves: the message store directory <data>/<domain>/<local part>; the Sieve tree; aliases in any domain that point at the account; distribution list memberships; recipients of mail still in the delivery queue; and quarantined mail, up to a ceiling of 200,000 rows. What does not move, on purpose. The archive and the message trace stay under the old address, as a record of the past. Erasure works by address, so erasing the new address will not reach the archive written under the old one: erase both (Compliance: personal data and erasure). The domains a role reaches are not rewritten either, so check a role after a cross-domain move (Administrator roles end to end). What is refused, before the first byte is moved: a domain this server does not hold, or one that is not active; a destination domain at its maximum number of accounts, or that would exceed its maximum total size; an account, an alias or a distribution list already at the new address; a mailbox encrypted at rest moving to another domain (409); a rename inside the domain is not refused; an installation whose message file names are still full paths; a credential that may not administer accounts in both domains; a mailbox in use. In use means a POP3 session, or an IMAP session with one of the account's folders selected. What is not counted: an IMAP session that has selected nothing, a webmail request in flight, a ManageSieve session and an SMTP delivery to the mailbox in flight. A message that arrives during the copy is written under the old directory, and the old directory is swept once afterwards; anything that could not be carried is HM6948. That is why a move of a large busy mailbox belongs out of hours. When a move fails or is interrupted There is no transaction across a filesystem and four database backends, so the order is chosen to make each step undoable: the store, then the account row, then the references, then the alias. A journal row is opened before the first step and closed after the last, and an interrupted move is said once per start as HM6947 in the application log, naming both addresses and the stage it stopped at. Stage What it means started Nothing has been changed moving the store The copy was under way; look at both directories. The database has not been touched store moved The directories are at the new address. While the account row says the old one, the account authenticates and its mail is unreadable account written The store and the account row agree; the references have not all been rewritten references rewritten Everything but the alias complete The move finished rolled back A step failed and everything before it was put back rollback failed Something could not be put back; HM6940 is in the application log To finish a move interrupted at moving the store or store moved, ask for the move again, naming the address the directories now have. Read the journal with hmctl mailbox status or GET /api/v1/accounts/<address>/move. The journal is not in the backup. Code Meaning HM6941 The move succeeded but the alias for the old address could not be created HM6942 Aliases pointing at the account could not be re-pointed HM6943 Distribution list memberships could not be moved HM6944 Messages in the delivery queue still name the old address HM6945 Quarantined messages still name the old address HM6946 The move could not be journalled. It still happened, and is on the audit trail Who may. manage_accounts, in both domains. The server administrator and a domain administrator of both domains have it; the help-desk role and the auditor do not. view_accounts is enough to read the journal. A move is one entry on the audit trail: object type account, action moved. What it does not do The relocation runbook does not repair the uninstall entry or the shortcuts, and it has no converter for the archive index. A mailbox move does not wait for a delivery in flight; nothing in the server records one. Setting Account.Address and saving goes through the same implementation, but leaves no alias, does not refuse while the mailbox is in use, and cannot cross domains. A domain rename (Domain.Name and Save()) is a different thing again, and keeps the path it has always taken. Where to read more Wiki: Relocating an Installation. Documents: RelocatingAnInstallation.md and MailboxMove.md. The public documentation: 33.1, the installation on disk. On this forum: Moving to new hardware, and why Windows to Linux is a rebuild, Upgrading to 6.3.4, and how the settings store works and when a save takes effect in Settings: what is not stock, when a save takes effect, and the three levels.
  • 0 Votes
    1 Posts
    370 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
    159 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
    140 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
    146 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
    171 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
    174 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
    140 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
    149 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.
  • 0 Votes
    1 Posts
    134 Views
    ProgressiverobotP
    Moving to new hardware is a restore onto fresh metal. Chapter 15 is the prerequisite, and the first time, rehearse onto a test machine. The plan (§33.2) Check the new machine against the chapter 3 checklist. Size the disk for the mail store you have now, not the one you had at installation. Install the same version, in the same path: a different version or directory is where these moves go wrong. Since hMailServer 5.4 a message row holds a bare file name and the path is rebuilt from the data directory; a database from before 5.4 can still hold full paths. Back up the old server. The built-in backup does not include messages unless you tick the option. For a large store, stop the service, copy Data\, dump the database natively, and take hMailServer.INI, Events\, your DKIM keys and, if one was made, an export of the stored-secrets key (--export-stored-secrets-key). Stop the old service for good and set it to Disabled. Anything it accepts after the backup is stranded. Restore the data directory, the database and hMailServer.INI, in chapter 15's order. Re-enter the protected secrets. Keep the host name if you can. Update the A record with a lowered TTL, get a PTR on the new IP, re-run the firewall rules, repoint your port forwards, and run Monitoring & troubleshooting, Diagnostics last. Leave the old machine powered off but intact for a week or two. It is the rollback. Budget for step 6. With ProtectStoredSecretsWithDPAPI=1, the default, stored passwords are encrypted with machine-scoped Windows DPAPI and cannot be decrypted on the new machine. Re-enter the database connection password with Bin\DBSetup.exe, then route, smart-host relayer and external-account passwords in the Control Panel. Forget this and you get a server that cannot reach its own database. Since 6.3.4 the stored-secrets key that seals second factors and the [Settings] secrets is kept in hMailServer.INI under the same DPAPI, so a copied INI does not carry it: import the exported file on the new machine with hMailServer.exe --import-stored-secrets-key <file> and restart the service. An upgraded server has no such key until an administrator makes one. Linux to Linux is easier: secrets sit under <DataFolder>/.hmailserver-secret-key, so copy that file too, preserving mode 0600 and its ownership. Windows to Linux is not a migration It is not supported and the project does not claim it is. The schema is the same on both, 6.3.4 needs 6104 either way, and the message store's on-disk format is the same. What is missing is a tested path: nobody has run a Windows installation onto Linux and verified the result (§35.14). What is known to break: SQL Server. An INI with Type=MSSQL or Type=MSSQLCE is refused by name. Move to PostgreSQL or MySQL on Windows first. Every DPAPI secret. Route, fetch-account and per-domain relay passwords, private-key passphrases and the administrator's TOTP secret. Each is reported once as HM6414 and answers empty. Event scripts and COM integrations. COM does not exist on Linux, and since 6.3.4 event scripts run there in JavaScript only: a VBScript file is rewritten and a JScript one ported, without COM objects. DKIM key paths. The per-domain key-file setting holds a Windows path such as C:\keys\mail.pem, which resolves to nothing on Linux. Copy the keys and re-point every signing domain. Directory case. The Linux build lower-cases directory names derived from an address; Windows leaves them as found. A store carrying Test.com is one this server looks for under test.com, and everything under it must end up owned by hmailserver. So treat it as a rebuild and a cutover. A new installation on Linux, accounts and mail brought over at mailbox level by mirroring folders over IMAP from the running Windows server. The Import Tool is not an option here: it is a Windows .NET program that drives the COM API, and Linux has neither (§33.8), verified against a still-running Windows server, DNS moved last.
  • 0 Votes
    1 Posts
    58 Views
    ProgressiverobotP
    Nothing in the server changes. The compiled server differs from 6.3.0 by its version stamp and one comment line. What changes is the installer: the Windows installer is now Authenticode-signed with Azure Artifact Signing, against a certificate profile issued to Progressive Robot Ltd. If you run with UpdateRequireAuthenticode=1, which has existed since 6.2.28, the server's own update path has been refusing every release of this project, because none carried a signature. From 6.3.1 the installer it downloads carries one. What the upgrade involved The orphan sweep in the 6029 to 6030 upgrade step runs children before parents. Before it adds seventeen foreign keys it deletes rows whose parent is gone, and three of those parent tables are pruned by the same deletes, so pruning an orphaned account, fetch account or distribution list re-orphans rows nothing revisits and the constraint that follows is refused. It affects only an upgrade from a schema below 6030 on a database that already holds orphaned rows, in all four database backends. No installation has reported hitting it, and when it fires it fails loudly with the engine's own words and rolls back. The fix is held for 6.3.2. The rest Only the Windows installer is signed. There is no Authenticode for a .deb, an .rpm or an AppImage. The elevation prompt now reads the publisher's name. SmartScreen still warns. Microsoft flags a signed installer as unrecognised until reputation accumulates. An EV certificate would not help; Microsoft removed EV's SmartScreen bypass in 2024. The UpdateRequireAuthenticode check is WinVerifyTrust on the downloaded installer, and it is Windows-only. On Linux the server says so rather than reporting a pass, and updating is the package manager's job. The signing gate asked for four of the six settings it guards and never asked about ARTIFACT_SIGNING_ENDPOINT or ARTIFACT_SIGNING_PROFILE. Three outcomes now: none of the six set is a silent no-op, all six signs, anything in between stops and names what is missing. 6.3.0's tagged run attached no Linux packages because the package-install check ran without sudo against /etc/hmailserver, which the package makes 0750 root:hmailserver. It runs under sudo now. Documentation: the README offers the Linux packages on its download line, and two files that named 6.2.29, a version that does not exist, now say 6.3.0. Full suite on the stamped binary: 2,175 tests, 2,166 passed, 0 failed, 9 skipped. Full release notes, checksums and signatures
  • 0 Votes
    1 Posts
    50 Views
    ProgressiverobotP
    If you run hMailServer on SQL Server Compact, the 6.2.25 installer told you the database could not be upgraded. It had been upgraded. Every Compact Edition upgrade through schema 6030 was reported as failed after it had succeeded (GitHub issue 114), and two seconds later the service ended and service recovery started it again. What the upgrade involved If the 6.2.25 installer failed on your database, that database is at schema 6030 with its foreign keys in place, and this installer finds nothing left to upgrade. If you restored a backup from before the attempt, the whole chain runs and the verification passes. From 6.2.24 or earlier, the 6.2.25 notes and the 6.2.24 notes before them describe what changes on the way, and everything there still applies. What was wrong After each upgrade script the database updater proves the schema changed by running a probe statement, and reads a failed probe as a missing object. The four probes for schema 6030, the foreign keys, used case when exists (subquery) in the SET expression. That is valid on SQL Server, MySQL and PostgreSQL. On SQL Server Compact it is an access violation inside the OLE DB provider, on a correct database with every constraint present. The server reported HM10045 Unknown error, the updater declared that Upgrade6029to6030MSSQLCE.sql had not created fk_hm_accounts_domain and blamed an [IGNORE-ERRORS] marker the statement does not carry. The fix The probes now read update hm_dbversion set value = value / (value - value) where not exists (select 1 from information_schema.table_constraints where constraint_name = '...' and constraint_type = 'FOREIGN KEY'). With the constraint present no row matches and nothing is evaluated. With it absent the one row matches and the division by zero fails the statement on every backend, leaving hm_dbversion untouched. The updater's message no longer asserts a cause it cannot see. It gives the backend's own words and says how to read them. This was reproduced from a Compact Edition database created at schema 6011 and upgraded with the shipped scripts: it reaches 6030 with all seventeen foreign keys, and the probe then faults the provider. build/check-db-scripts.ps1 now runs every probe through the provider the server uses, with a negative control that must fail, and a regression fixture runs them through the same COM path the updater takes. Both fail on the 6.2.25 statement. Full release notes, checksums and signatures
  • 0 Votes
    1 Posts
    65 Views
    ProgressiverobotP
    6.2.20 fixes a fresh-install failure introduced in 6.2.19. Database creation failed on a new install, and the installer then hung instead of reporting the failure. Fresh installations of 6.2.19 on the built-in database, which is the default, were affected, and most likely fresh installations onto MySQL. What the upgrade involved Upgrades of an existing installation were never affected. Upgrades run the upgrade scripts, and those were clean, and existing servers already have their databases. Only fresh installs hit this. If an unattended 6.2.19 install is still sitting there doing nothing, it is waiting on a message box. What was wrong The SQL script runner splits a script into commands on blank lines. SQL Server Compact, the embedded database a fresh install uses, executes one statement per command and refuses a batch. A recent change added settings to the create scripts with a single newline between inserts, so three statements arrived as one command. Full SQL Server accepts that batch, so the development database and the 1,490-test regression suite saw nothing wrong. The same adjacency was in the MySQL and PostgreSQL create scripts. All three are fixed, and a mechanical sweep of all 199 SQL scripts confirms no multi-statement command remains in any create script or any SQL CE script. Five upgrade scripts from the 5.x era keep their batches for the full-server backends that accept them. The error raised a plain message box, and /VERYSILENT /SUPPRESSMSGBOXES suppresses only the suppressible kind. A silent install did not report the failure, it waited on a dialog with nobody at the keyboard. In CI that was ninety minutes until a person cancelled it. All 28 dialogs in the installer now take their default button under /SUPPRESSMSGBOXES, and the installer proceeds to an exit code a deployment script can read. Interactive installs see exactly the dialogs they always saw. Also in this release IMAP THREAD (RFC 5256), both algorithms, THREAD=ORDEREDSUBJECT and THREAD=REFERENCES, with real References-chain threading. It is bounded by the same per-command ceilings as SEARCH and SORT. An adversarial review found and fixed a stack overflow reachable through a deep reply chain, an uninterruptible header pass and a malformed-Date sort inversion. The installer smoke test now bounds the silent-install step at ten minutes, keeps the installer's own log, and on a database failure re-runs the setup tool and prints its stderr. Full release notes, checksums and signatures
  • 0 Votes
    1 Posts
    53 Views
    ProgressiverobotP
    The Control Panel raised an error dialog every time you dismissed the Ctrl+K settings palette (GitHub issue 21, reported by grumpymojo). It was present in both 6.2.14 and 6.2.15, and because choosing a result and pressing Esc both went through the same path, it appeared on essentially every search rather than on any particular search term or configuration. What the upgrade involved No server change and no database change. Schema version stays at 6005. Anyone coming from 6.2.14 or earlier should read the 6.2.15 notes as well, because everything in them applies here. Fixed Closing the palette moves the focus away, which raised the deactivation handler while the close was still in progress, and that handler closed the window a second time. WPF refuses that outright: System.InvalidOperationException: Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing. The deactivation handler now closes the palette only when a close is not already under way. Nothing was lost to this defect. The navigation had already happened by the time the dialog appeared, so no setting was misapplied. It was noise in the one feature added to make settings easier to find. Still open at this release Discussion #18, the stall after 354 when relaying from Postfix or Proxmox Mail Gateway, remained open and unreproduced. 6.2.15 added debug logging to the SMTP DATA path that distinguishes the three candidate stages, and that log is what will identify the cause. Tracked in #20. Validated by the full regression suite: 1040 of 1040 passing, zero failures. The download is hMailServer-6.2.16-x64.exe. Full release notes, checksums and signatures