Repository navigation
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.
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.
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 listConnect-HmServer -Url https://mail.example.com:8045 -ApiKey (Get-Content .\api.key)
Get-HmDomainhmctl 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 [--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.
hmctl accounts import new-people.csv --dry-run
hmctl accounts import new-people.csv
hmctl accounts export example.com accounts.csvThe 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.
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 -WhatIfEvery 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.
- It does not read or write the message store. Mail is IMAP's business.
-
It is not a backup tool.
hmctl backup startasks the server to run its own backup, and that is all. -
It sets no administrator password.
hmailserver --set-admin-passwordon 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.pyfails if they stop doing so.
hMailServer 6.3.8, the latest release (2 November 2026, schema 6108) · AGPL-3.0-or-later · Repository · Mirror on GitHub · Report a documentation error (a GitLab issue naming the page)
Home — full index
Start here
1. Install and run
- Before You Install
- Installing hMailServer
- Installing on Linux
- Running in a Container
- Profiles and Environment
- The Control Panel
- The Control Deck
- Your First Domain and Mailbox
- Connecting a Mail Client
- The Webmail
- DNS for Your Domain
- Migration
2. Secure it
- Encryption and Certificates
- Database TLS
- Encryption for People
- Stopping Spam
- Stopping Viruses
- Security Hardening
- Secure Defaults
- The Service Account
- Secrets at Rest
- Sign-in and Identity
- Security Policy
3. Operate it
- Monitoring and Health
- The SNMP Agent
- Backup and Restore
- Message Storage
- Compliance and eDiscovery
- Troubleshooting
- Diagnosing Stalled Mail
- Relocating an Installation
- Upgrading hMailServer
- Upgrading Guide
- The Update Feed
- Migrating the Database Backend
- High Availability Runbook
- Warm Standby
- Runbooks Digest
4. Extend it
- Rules and Sieve
- Aliases Lists and Public Folders
- Rooms and Equipment
- Mailing Lists
- Routes and Relays
- Mail In to a Webhook
- Outbound Shaping
- The COM API and Scripting
- Event Scripts
- The REST API
- Command Line
- APIs Reference
5. Contribute to it
- Project Handbook
- Architecture
- Contributing
- Release Process
- Governance
- Assurance Case
- Regression Test Environment
- Fuzzing
- Regulatory Scope
- Third-Party Binaries
Look it up — from any journey
- Settings Reference
- Settings Overview
- Capabilities and Configuration
- Control Panel Reference
- Control Panel Pages
- Ports Reference
- Changes-Since-6210
- Version-History-to-6210
- Glossary
About this wiki
- hMailServer 6.3.8 is the latest release (2 November 2026, schema 6108)
- Pages say since 6.3.4 where a feature is new in it
- AGPL-3.0-or-later
- Repository, the one place the project is worked on
- Its mirror on GitHub, since 18 October 2026, with no Issues
- Questions: a GitLab issue with the Question template, GitHub Discussions Q&A, or the forum
- Report a documentation error: a GitLab issue naming the page