Skip to content

Command Line

chrisholloway5 edited this page Sep 15, 2026 · 17 revisions

Command line

In the tree since 15 September 2026, unreleased: both clients are in the repository and the Linux packages install hmctl, but no published release carries them yet.

Source of truth: hmailserver/docs/CommandLine.md, kept in the repository and copied here.

Administering the server from a command line

Two clients, one vocabulary, one API: hmctl for any shell, and the HmailServer PowerShell module for PowerShell. Both speak the REST API, so both work against a Windows server, a Linux server, or a server on another machine, and neither needs COM or the Control Panel.

This is not a replacement for either administration program. The Windows Control Panel is the place to configure a server, and the browser Control Deck is that place on Linux. A command line is for the things a command line is better at: making fifty accounts from a spreadsheet, reading one setting in a script, watching the delivery queue, asking what a rule criterion would decide, or doing the same thing to twenty servers.

hmailserver/source/Tools/Cli/hmctl One Python 3 file, no dependencies beyond the standard library. The Linux packages install it as /usr/bin/hmctl.
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 and asserts what each one sent, so the two cannot drift apart in what they ask for. In CI.

build/hmconfig.ps1 is a different tool and stays: it exports the whole configuration as one reviewable JSON document and applies it to another machine, over COM, on Windows. These two are per-object verbs over HTTP.

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 wherever you type them - 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, rather than waiting at a prompt nobody can see.

--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 not a convenience, and this will not make it one. Give the server a certificate the client trusts.

hmctl

hmctl [--url URL] [--api-key KEY | --password PASSWORD | --password-file FILE]
      [--insecure] [--json] <resource> <verb> [arguments]
Resource Verbs Notes
domain list, get, create, update, delete
account list, get, create, update, delete Listed and created under --domain, read and written at the address: which is how the API is shaped.
alias list, create, delete Under --domain.
list list, create, update, delete Distribution lists, under --domain.
group list, get, create, update, delete Account groups; member handles the accounts in one.
rule, route, port, iprange, certificate list, create, update, delete Server-wide.
fetchaccount list, get, create, update, delete Under --address.
apikey list, create, delete Administrator password only, never an API key.
queue list, retry, delete
quarantine list, release, delete
settings get, set A group whole, or one setting: settings, antispam, antivirus, logging, backup, cache, indexing, scripting, directories.
ini list, get, set, delete The hMailServer.ini keys the server exposes.
log list, tail
backup status, start The server's own backup.
app-password list, create, delete Under an account's address.
match (a criterion and a value) What a rule criterion would decide, without a rule.
accounts import, export A CSV file, with a dry run.
api (method and path) The escape hatch, for a route newer than this file.

A few whole commands:

hmctl domain create --set name=example.com --set active=true
hmctl account create --domain example.com --set address=ann@example.com --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 queue list --json | jq '.[] | select(.retry_count > 3)'
hmctl match contains viagra "cheap viagra here"
hmctl api POST /api/v1/server/reinitialize

--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 is a table by default and the server's own JSON with --json. The exit code is 0 for success, 1 for an error, 2 for a usage mistake, 3 when the server refused the credential and 4 when what you named does not exist - so a script can tell "no such account" from "wrong password" without reading the text.

Accounts from a spreadsheet

hmctl accounts import new-people.csv --dry-run
hmctl accounts import new-people.csv
hmctl accounts export example.com accounts.csv

The header row names the fields and address is the only one required; anything the account resource takes may be a column, so the file does not need changing when the account gains a field.

address,password,name,active,max_size_mb
ann@example.com,Sup3rSecret!,Ann Adams,true,500
bob@example.com,An0therOne!,Bob Brown,true,

An empty cell is not sent, so the server's own default applies. An address that already exists is left alone and counted, never overwritten: an import that silently reset a password would lock a hundred people out with one command. --dry-run makes no request that changes anything and prints what would happen, line by line.

The export writes no password column, because the server does not have the passwords to give. A file exported and re-imported makes no accounts; it is for an inventory, not a round trip.

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 ann@example.com -Password (Read-Host -AsSecureString)
Set-HmAccount -Address ann@example.com -Property @{ max_size_mb = 500 }
Set-HmSetting -Group antispam -Property @{ use_spf = $true }
Get-HmQueue | Where-Object retry_count -gt 3 | Start-HmQueueDelivery
Import-HmAccount -Path .\new-people.csv -WhatIf

Every function answers objects, so Where-Object, Sort-Object, Export-Csv and the rest work on them; 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 itself 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.

What neither client does

  • It does not read or write the message store. Mail is IMAP's business.
  • It is not a backup tool. hmctl backup start asks the server to run its own backup, and that is all.
  • It sets no administrator password. hmailserver --set-admin-password on the server does that, and the Control Panel does it on Windows.
  • Neither invents a field. A field you did not give is one the server decides; the two clients were made to send the same request for the same command, and build/check-cli.py fails if they stop doing so.

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