Skip to content

The Control Deck

Christopher Holloway edited this page Oct 19, 2026 · 13 revisions

The Control Deck

Written on 24 September 2026 from the root README's Administration section, hmailserver/docs/DeckParity.md (the census a checker regenerates from the sources) and the operator documents it names, and brought up to 6.3.4 (27 September 2026, schema 6104), on 26 September 2026. What is marked since 6.3.4 is in 6.3.4 and not in 6.3.3. Brought up on 5 October 2026 to 6.3.6 (6 October 2026, schema 6108), for what 6.3.6 changes on this page. 6.3.7, the latest release (17 October 2026, schema 6108), is not yet reflected here. 6.3.5's quarantine view - the HELO column, the grouping and its own Yes or No - is not yet written in.

The Control Deck is the browser administration page: the front end of the REST API, served at / by the same listener as the webmail. On Linux, where there is no Control Panel and no COM, it is the administration people click through; on Windows it stands beside the desktop Control Panel (The Control Panel) and reaches a server from any machine with a browser. It keeps no password, runs nothing of its own, and every change it makes is a REST call whose answer it reads back - so what it shows is what the server holds, and a refusal is shown in the server's own words beside the control that caused it.

flowchart LR
    A["Administrator's browser"] -->|"HTTPS, one session cookie"| R["REST listener<br/>RestApiPort"]
    R --> D["/ - the Control Deck"]
    R --> API["/api/v1/... - every route it calls"]
    API --> S["The server: domains, accounts,<br/>queue, settings, logs"]
    CP["Control Panel (Windows)"] -->|"COM"| S
    H["hmctl, PowerShell"] -->|"REST"| API
Loading

Contents

Turning it on and signing in

The Deck needs the REST listener: RestApiPort set (8045 by convention), a certificate unless the listener is bound to loopback, and a name people can reach - the same three things the webmail needs (The Webmail). On Linux the package installs the page with the server (Installing on Linux step 7 and 8); a server that answers "Web administration page not installed" is missing the WebAdmin files beside the binary and falls back to a built-in stub.

Open https://<host>:8045/ and sign in:

  • as Administrator with the administrator password, and the one-time code when a second factor is enrolled - the password is sent once and exchanged for an HttpOnly, SameSite=Strict session cookie that ends after thirty idle minutes or twelve hours;
  • since 6.3.4, as an account that holds an administrator role, which sees only what the role allows (Security Hardening §17.4);
  • since 6.3.4, with a passkey the administrator enrolled on the Deck's Passkeys view, or through the organisation's OpenID Connect or SAML identity provider for an address on the server's list of Deck administrators (Sign-in and Identity).

It speaks twenty languages beside English - the browser's, or the one chosen in its header - and has a light theme, a dark one and the system's. It is usable with no pointer: / searches the view, ? lists the shortcuts, the arrows and Enter move through and open a table's rows.

What it covers

In 6.3.3 it has fifteen views - the dashboard, domains and their accounts, distribution lists, external (fetch) accounts, IP ranges, rules, routes, certificates, listeners, the settings groups, the backup, the delivery queue, messages, logs and TLSA records - and writes what they show: since 9 September 2026 a domain is created, edited in every field the Control Panel's domain pages hold and deleted, the settings groups are drawn from the server's own OpenAPI document with each setting's type and permitted words, and a button restarts the services in place so that a new listener takes effect.

Since 6.3.4 it has 40 views. The twenty-five added:

View What it is for
Checklist The first-hour checklist the server judges itself, the four setting profiles with their difference from this server, and the sample domain (below)
API keys The keys other programs hold, made with a scope, a role, the domains they may reach and an expiry; the token shown once
Roles The four built-in administrator roles, custom roles, and who holds which in which domains
Quarantine Held mail, released to its recipients or deleted
Held for review Submissions compromised-account detection kept out of the queue, released or discarded
Archive The archive searched by domain, mailbox, sender, recipient, subject and time, with legal holds that survive the retention sweep and any erasure
Discovery eDiscovery cases: open and close a case, search every mailbox, the deleted items and the archive by custodian, words, dates and attachment, place and lift holds on an account, a domain or the archived copies a search found, and export and download what was found. Needs view_discovery to read and manage_discovery to change; a domain administrator is not shown it (Compliance and eDiscovery)
Journaling The journal and dual-delivery rules
Audit trail Every administrative change as a row of a hash chain, Verify the chain, and Put back on a setting's row
Changed from stock Every setting that differs from what this version ships, and where each value came from - the administrator, a profile, the environment
Metric history The stored metrics as a chart
Reports Traffic, failures, senders, recipients, mailboxes, volume and storage per domain and per day, and who is nearest their quota
Reputation Whether this server's outbound addresses are on a blocklist
Spam traps A domain's trap addresses and what has written to them
Webhooks Outbound webhooks and their deliveries
Inbound webhooks Addresses and domains whose mail is posted to a URL: the routes, whether each posts only or posts and delivers, and the last twenty-five deliveries with a kept message's Download, Retry and Remove (Mail In to a Webhook)
Devices What has signed in as an account, and revoking it
Remote domains The per-domain policies: TLS in each direction, limits, recipient verification, gateway encryption
Groups The account groups a folder permission names
Folders A folder's access-control list, granted and revoked by account, group or anyone
Migrations Mailboxes copied from another IMAP server, Google Workspace or Microsoft 365, a job per mailbox (Migration)
Update The release feed, download and verification against the Sigstore bundle, and the install, with Drain and Resume. Since 6.3.4 the default feed is https://updates.progressiverobot.com, which GitLab Pages serves from the project's releases, in place of the suspended account's Releases API on GitHub, which has answered 404 since 18 September 2026. 6.3.4 itself carries no Sigstore bundle, so no server's live update installs it: the view says it cannot be verified and downloads nothing, and 6.3.4 is installed by hand. The releases after 6.3.4 are signed by the project's GitLab CI, whose signing job has made every release since 6.3.5, and a 6.3.4 server's live update trusts them: 6.3.5 at the path the project began at, and every release from 6.3.6 at the path of the group hmailserver, which the project moved to on 29 September 2026. A server from 6.2.28 to 6.3.3 still reads the suspended account's Releases API on GitHub until UpdateFeedUrl=https://updates.progressiverobot.com/releases.json is set (UpdateFeed.md, Upgrading hMailServer). On Windows, a server running as NT SERVICE\hMailServer - a new installation's account - is refused the apply, and an administrator runs the verified installer
DNS records The records each domain should publish, in one block to paste at a registrar
Diagnostics The desktop program's diagnostic run over a route of its own
Passkeys The administrator's own passkeys

Several existing views also grew: the dashboard shows a Message store section - the messages, what their files take, what the parts held once take, and the saving the server last measured (Message Storage); the queue page shows Slowed destinations (the outbound back-off), the connection counts, the sandbox's held messages and the IP warm-up schedules and counts (Outbound Shaping); the logs page lists every open connection and can follow one live; the domain editor carries the report mailbox and the DMARC chart, the quota policy, restricted delivery, impersonation protection, the S/MIME authority, the Web Key Directory switch and the domain's Mailbox recovery key card; the account editor carries the directory entry, recovery, zero-access and a person's .zip export, the account's kind (a person, a room or equipment) with a Booking policy card (Rooms and Equipment) and the Encryption at rest card (Message Storage); the lists editor carries the membership query, Delivers to now and the list manager's member table; and the Settings view marks each field with when a save applies, shows a pending bar with Apply now, and marks a setting the server's environment manages as Set by the environment with no control (Profiles and Environment); its server form also carries the message store's compression switch, the object store's settings and the PST import directory, none of which has a Control Panel field but the first (Message Storage, Migration). The migrations view takes PST jobs as well as IMAP, Google and Microsoft 365 ones.

Parity with the Control Panel

build/check-deck-parity.py measures, from the sources alone, how far the Deck and the REST API are from the desktop Control Panel, and writes DeckParity.md. In 6.3.4:

Measure Count
COM properties the desktop program writes 466
of them writable over REST 458
of them reachable from a Deck view 458
REST routes (path and method) 586, 258 of them writes
Deck views 40 (15 in 6.3.3)

The eight "missing" are an account's directory fields - title, department, phones, photo - which PUT /api/v1/accounts/{address}/directory does carry, under names the census's matching does not see; read the document's What the heuristic gets wrong before calling a row a gap. In 6.3.3 the count was 322 of the 330 properties the desktop program wrote.

The first hour

Since 6.3.4. The Checklist view is the server's own judgement of what a new server still needs - thirteen items since 6.3.6, No password on port 25 the new one; twelve before - each done, action needed, cannot tell or not needed, with a sentence saying what it found:

Item Done when
A profile for this server A setting profile has been applied, on a young server (asked for only while it has fewer than ten accounts)
A domain, an account At least one of each exists
TLS on the submission port Every SMTP listener other than port 25 has STARTTLS or implicit TLS and a certificate - other than the self-signed one a new installation made, which mail clients warn about
Passwords only over TLS Every IP range that lets a client connect from another machine requires TLS for authentication, or reaches only listeners that start TLS before a password. A new 6.3.4 installation is set up so; an upgraded one is judged as it is
SPF, DKIM, DMARC Every active domain publishes the record, and for DKIM signs with a key whose record is there
A backup destination Set, and the folder exists
A second factor for the administrator The administrator password is enrolled with a one-time code
The stored-secrets key This server holds the key the database records, and an export of it has been recorded; not needed with sealing switched off (Security Hardening)
The account the server runs as Not LocalSystem on Windows, not root on Linux - a recommendation, never an error; an upgraded Windows installation keeps the account it had

Under it, the four setting profiles - office, gateway, relay, sender - each with its difference from this server before anything changes, applied with a confirmation where it would close a listener or weaken a check, and put back with one button (Profiles and Environment); and a button that makes a throwaway sample domain, example.test, with three mailboxes, a list, a rule and eight delivered messages, and another that removes it (Your First Domain and Mailbox).

Who may use it

The administrator password reaches everything. Since 6.3.4 an account signed in with an administrator role sees the views its permissions allow and is refused the rest in the server's words: a domainadmin its own domain's accounts, lists and quarantine; a helpdesk finding an account, resetting its password, unlocking it and reading where its mail went; an auditor every read, the audit trail included, and no write. A role confined to named domains is refused the server-wide views - the queue, the logs, the settings, the audit trail. An API key is not a way into the Deck: it is for programs, and the audit trail and the alerts refuse every key whatever its role.

What it does not do

  • It is not the desktop Control Panel in a browser. A few things are the Control Panel's alone - editing the event scripts, which are a file on the server's disk, and the Active Directory pickers among them - and some views are the Deck's alone (the migrations, the reports, the webhooks, the inbound webhooks). eDiscovery is read on both but worked only here: the Control Panel's Discovery page reads the cases and changes nothing. The alert rules have a page on neither: they are REST and hmctl api.
  • It keeps no state of its own. A reinitialise ends its session, because the REST listener it was using restarts; sign in again.
  • Arabic, Hebrew and Persian are the webmail's three right-to-left languages and are not among the Deck's.
  • No API key signs in to it, and it does not store the administrator password in the browser.
  • Since 6.3.6: the page is served under a script policy that names the SHA-256 of its one inline script, with Trusted Types, X-Frame-Options and Cross-Origin-Opener-Policy, so a proxy in front of the server must pass it through unchanged (6.2.28 to 6.3.5 allowed any inline script on the page); an address the server hands it is drawn as a link only when it is http or https; a single sign-on error is shown only in a tab that started a sign-in in the last hour, and the pending half of a sign-in is carried by the browser, sealed; a translation that is not text is left out; the Update page shows a sentence where its Install button stood when the service runs as NT SERVICE\hMailServer; the Lists view has the never-ban list by reverse name, and the Ports view each SMTP listener's SMTP authentication; the quarantine's HELO column and grouping arrived in 6.3.5.

See also: The Control Panel · The REST API · Command Line · APIs Reference · Installing on Linux

Home — full index

Start here

1. Install and run

2. Secure it

3. Operate it

4. Extend it

5. Contribute to it

Look it up — from any journey


About this wiki

Clone this wiki locally