<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[hmctl and the PowerShell module: shell administration and accounts from a CSV file]]></title><description><![CDATA[<p dir="auto"><em>Written for 6.3.4. hMailServer 6.3.6 (6 October 2026) carries the PowerShell module, <code>HmailServer.psd1</code> and <code>HmailServer.psm1</code>, Authenticode-signed, as files of the release, and Linux packages for x86-64 and AArch64; <code>hmctl</code> as a PyPI package and the module on the PowerShell Gallery are built but not yet published. See <a href="/topic/28276">the 6.3.6 announcement</a>. 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 <a href="https://github.com/RainbowWizard-coder/hMailServer" rel="nofollow ugc">mirrored on GitHub</a>, 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 <code>hmctl</code> 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 <a href="/topic/28277">the 6.3.7 announcement</a>. Since 21 October 2026 <code>hmctl</code> is on PyPI as <code>hmailserver-cli</code> and the module on the PowerShell Gallery as <code>HmailServer</code>, 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 <code>hmctl api</code> or <code>--from-json</code> must be one JSON object that names each member once, and 6.3.8's <code>hmctl</code> refuses a body holding <code>NaN</code> or <code>Infinity</code> before sending it. See <a href="/topic/28278">the 6.3.8 announcement</a>.</em></p>
<p dir="auto">hMailServer 6.3.4 has two command-line clients: <code>hmctl</code> for any shell, and the <code>HmailServer</code> 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. <strong>The one thing to know first:</strong> 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.</p>
<p dir="auto">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.</p>
<h3><a class="anchor-offset" name="where-the-clients-come-from"></a>Where the clients come from</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>File</th>
<th>What it is</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>hmailserver/source/Tools/Cli/hmctl</code></td>
<td>One Python 3 file with no dependencies beyond the standard library.</td>
</tr>
<tr>
<td><code>hmailserver/source/Tools/Cli/HmailServer.psm1</code></td>
<td>The PowerShell module: <code>Get-HmDomain</code>, <code>New-HmAccount</code>, <code>Set-HmSetting</code> and the rest, answering objects rather than text.</td>
</tr>
<tr>
<td><code>build/check-cli.py</code></td>
<td>Runs both against a recorded server in CI and asserts what each one sent, so the two cannot drift apart.</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>On Linux</strong>, the packages install <code>hmctl</code> as <code>/usr/bin/hmctl</code>. The 6.3.4 packages are x86-64 only, and the changelog records Python 3 as one of the <code>.deb</code>'s two Recommends, the other being 7-Zip. <strong>On Windows</strong>, the wiki says the installer carries neither client: take them from the repository directory <code>hmailserver/source/Tools/Cli/</code>. Both clients are in the repository since 6.3.4.</p>
<p dir="auto"><code>build/hmconfig.ps1</code> 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.</p>
<h3><a class="anchor-offset" name="before-anything-else-the-listener"></a>Before anything else: the listener</h3>
<p dir="auto"><strong>On Linux</strong>, store the port on the server itself and restart the service. Since 6.3.4 the database is the settings store, so editing the <code>[Settings]</code> section of <code>hMailServer.ini</code> does not turn the listener on once the server has stored the key.</p>
<pre><code class="language-bash">sudo hmailserver --set-setting RestApiPort=8080
sudo systemctl restart hmailserver
</code></pre>
<p dir="auto"><strong>On Windows</strong>, the Control Panel does this over COM.</p>
<p dir="auto"><strong>On either platform</strong>, 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. <a href="/topic/28264">The REST API guide</a> has the listener's settings, the reinitialise they wait for and the three ways it refuses to start, and <a href="/topic/28245">the hmailserver command line on Linux</a> has the checks <code>--set-setting</code> makes and how it takes a secret.</p>
<h3><a class="anchor-offset" name="credentials"></a>Credentials</h3>
<p dir="auto">Either the administrator password, sent as HTTP Basic with the user name <code>Administrator</code>, or an API key, sent as a bearer token. <strong>An API key is the better answer for anything unattended</strong>: it can be read-only, it can be restricted to one domain, and it can be revoked without changing the administrator password.</p>
<pre><code class="language-bash">export HMAIL_URL=https://mail.example.com:8045
export HMAIL_API_KEY="$(cat /etc/hmailserver/api.key)"
hmctl domain list
</code></pre>
<pre><code class="language-powershell">Connect-HmServer -Url https://mail.example.com:8045 -ApiKey (Get-Content .\api.key)
Get-HmDomain
</code></pre>
<p dir="auto"><code>hmctl</code> reads <code>HMAIL_URL</code>, <code>HMAIL_API_KEY</code>, <code>HMAIL_PASSWORD</code> and <code>HMAIL_PASSWORD_FILE</code>, and takes <code>--url</code>, <code>--api-key</code>, <code>--password</code> and <code>--password-file</code> 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.</p>
<p dir="auto"><strong><code>--insecure</code> is refused for anything but the loopback.</strong> A self-signed certificate on <code>localhost</code> 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.</p>
<p dir="auto">Some commands take the administrator password and never an API key, because no key reaches the routes behind them: <code>hmctl apikey</code>, <code>hmctl ini</code>, <code>hmctl config changed</code>, <code>hmctl webhook</code> and <code>hmctl webhooks</code>. 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; <a href="/topic/28264">the REST API guide</a> has the detail.</p>
<h3><a class="anchor-offset" name="hmctl"></a>hmctl</h3>
<pre><code>hmctl [--url URL] [--api-key KEY | --password PASSWORD | --password-file FILE]
      [--insecure] [--json] &lt;resource&gt; &lt;verb&gt; [arguments]
</code></pre>
<p dir="auto">The resources, grouped. <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/CommandLine.md" rel="nofollow ugc">CommandLine.md</a> has a row for each.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Area</th>
<th>Resources</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Domains and what is in them</td>
<td><code>domain</code>, <code>account</code>, <code>alias</code>, <code>list</code>, <code>group</code>, <code>fetchaccount</code>, <code>app-password</code>, <code>device</code>, <code>mailbox</code></td>
<td>An account is listed and created under <code>--domain</code>, and read and written at its address, which is how the API is shaped. <code>mailbox</code> renames or moves one.</td>
</tr>
<tr>
<td>Server-wide objects</td>
<td><code>rule</code>, <code>route</code>, <code>port</code>, <code>iprange</code>, <code>certificate</code>, <code>journalrule</code></td>
<td>list, create, update, delete</td>
</tr>
<tr>
<td>Mail in motion</td>
<td><code>queue</code>, <code>quarantine</code></td>
<td><code>queue</code> has list, retry, delete; <code>quarantine</code> has list, release, delete</td>
</tr>
<tr>
<td>Settings</td>
<td><code>settings</code>, <code>ini</code>, <code>config</code></td>
<td>A settings group whole or one setting; the keys that were the <code>[Settings]</code> section; the configuration as one document</td>
</tr>
<tr>
<td>Deliverability</td>
<td><code>dkim</code>, <code>dmarc</code>, <code>suppression</code>, <code>bounce</code>, <code>bounces</code>, <code>complaint</code>, <code>complaints</code>, <code>spamtrap</code>, <code>traphits</code>, <code>reputation</code>, <code>openpgp</code></td>
<td><code>reputation</code> exits 1 when an address is listed and when a list could not be asked</td>
</tr>
<tr>
<td>Integrations</td>
<td><code>apikey</code>, <code>webhook</code>, <code>webhooks</code></td>
<td>All three with the administrator password only, never an API key</td>
</tr>
<tr>
<td>Operations</td>
<td><code>log</code>, <code>backup</code>, <code>report</code>, <code>sample-domain</code>, <code>match</code></td>
<td><code>match</code> says what a rule criterion would decide, without a rule</td>
</tr>
<tr>
<td>Bulk</td>
<td><code>accounts</code></td>
<td>import and export, a CSV file, with a dry run</td>
</tr>
<tr>
<td>Anything else</td>
<td><code>api</code></td>
<td>A method and a path: the escape hatch, for a route newer than the client</td>
</tr>
</tbody>
</table>
<p dir="auto">A few whole commands:</p>
<pre><code class="language-bash">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 device ann@example.com list
hmctl queue list --json | jq '.[] | select(.retry_count &gt; 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
</code></pre>
<p dir="auto"><strong>How values are read.</strong> <code>--set name=value</code> reads <code>true</code>, <code>false</code>, <code>null</code>, numbers and anything starting with <code>{</code> or <code>[</code> as JSON, and everything else as a string. <code>--set-string</code> keeps a value that looks like one of those a string. <code>--from-json FILE</code> takes a whole object, and <code>-</code> reads it from standard input.</p>
<p dir="auto"><strong>Output and exit codes.</strong> Output is a table by default and the server's own JSON with <code>--json</code>.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Exit code</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>Success</td>
</tr>
<tr>
<td>1</td>
<td>An error</td>
</tr>
<tr>
<td>2</td>
<td>A usage mistake</td>
</tr>
<tr>
<td>3</td>
<td>The server refused the credential</td>
</tr>
<tr>
<td>4</td>
<td>What was named does not exist</td>
</tr>
</tbody>
</table>
<p dir="auto">A script can therefore tell "no such account" from "wrong password" without reading the text.</p>
<h3><a class="anchor-offset" name="the-powershell-module"></a>The PowerShell module</h3>
<pre><code class="language-powershell">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
Get-HmChangedSetting | Format-Table store, name, value, default
</code></pre>
<p dir="auto">Every function answers objects, so <code>Where-Object</code>, <code>Sort-Object</code> and <code>Export-Csv</code> work on them, and nothing in the module formats anything. Everything that changes the server supports <code>-WhatIf</code> and <code>-Confirm</code>, 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 <code>Connect-HmServer</code> has run. <code>Invoke-HmApi -Method GET -Path /api/v1/...</code> is the escape hatch, and is what every other function is built on.</p>
<h3><a class="anchor-offset" name="accounts-from-a-csv-file"></a>Accounts from a CSV file</h3>
<pre><code class="language-bash">hmctl accounts import new-people.csv --dry-run
hmctl accounts import new-people.csv
hmctl accounts export example.com accounts.csv
</code></pre>
<pre><code class="language-powershell">Import-HmAccount -Path .\new-people.csv -WhatIf
Import-HmAccount -Path .\new-people.csv
Export-HmAccount -Domain example.com -Path .\accounts.csv
</code></pre>
<p dir="auto">The file is UTF-8 (in Excel, <em>CSV UTF-8</em>) and its header row names the columns. These are the columns, and there are no others.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Column</th>
<th>Required</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>address</code></td>
<td>yes</td>
<td>Created under the address's own domain.</td>
</tr>
<tr>
<td><code>password</code></td>
<td>yes</td>
<td>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.</td>
</tr>
<tr>
<td><code>first_name</code></td>
<td></td>
<td>At most 60 characters, counted as UTF-16 code units.</td>
</tr>
<tr>
<td><code>last_name</code></td>
<td></td>
<td>As <code>first_name</code>.</td>
</tr>
<tr>
<td><code>active</code></td>
<td></td>
<td><code>true</code>, <code>yes</code>, <code>on</code> or <code>1</code>; <code>false</code>, <code>no</code>, <code>off</code> or <code>0</code>. Default true.</td>
</tr>
<tr>
<td><code>max_size_mb</code></td>
<td></td>
<td>A whole number of megabytes; <code>0</code> is no limit, and is the default.</td>
</tr>
<tr>
<td><code>admin_level</code></td>
<td></td>
<td><code>user</code>, <code>domain</code> or <code>server</code>. Default <code>user</code>.</td>
</tr>
</tbody>
</table>
<pre><code class="language-csv">address,password,first_name,last_name,active,max_size_mb,admin_level
ann@example.com,S0me-Long-Passphrase,Ann,Adams,true,500,user
bob@example.com,An0ther-Long-Passphrase,Bob,"Brown, Jr",true,,domain
</code></pre>
<p dir="auto">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.</p>
<p dir="auto"><strong>What is refused before anything is sent.</strong> Each row becomes one <code>POST /api/v1/domains/&lt;domain&gt;/accounts</code>, 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 <code>name</code> column included. The clients also refuse, in the words the server would use:</p>
<ul>
<li>a row with no password;</li>
<li>an <code>active</code> that is not one of the eight words;</li>
<li>a <code>max_size_mb</code> that is not a whole number;</li>
<li>an <code>admin_level</code> that is not one of the three;</li>
<li>a name past its 60 characters;</li>
<li>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.</li>
</ul>
<p dir="auto"><strong>What a dry run cannot foresee.</strong> 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; <code>hmctl</code> adds the line number.</p>
<p dir="auto"><strong>An address that already exists is left alone and counted</strong>, never overwritten. An import that reset a password without saying so would lock people out with one command. <code>--dry-run</code> makes no request that changes anything and prints what would happen, line by line. The exit code is 1 when any row was refused.</p>
<p dir="auto"><strong>Who may make an administrator.</strong> <code>admin_level</code> 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 <code>server</code> is refused with a <code>403</code>.</p>
<p dir="auto"><strong>The export</strong> writes the same columns less <code>password</code>, 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.</p>
<h3><a class="anchor-offset" name="the-configuration-as-one-document"></a>The configuration as one document</h3>
<pre><code class="language-bash">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
</code></pre>
<p dir="auto">The document holds the settings groups, the <code>hMailServer.ini</code> 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.</p>
<p dir="auto"><code>apply</code> is a plan unless <code>--force</code> is added, and <strong>a deletion needs <code>--allow-delete</code> on top of that</strong>. Without it, an entry the server has and the document does not is left alone and counted.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Not carried</th>
<th>Why</th>
</tr>
</thead>
<tbody>
<tr>
<td>Passwords</td>
<td>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 <code>password</code> field for the accounts to be created.</td>
</tr>
<tr>
<td>The secrets among the ini keys</td>
<td>Listed as <code>(set)</code> or empty, and left out of the document. <code>hmctl ini get &lt;name&gt;</code> reads one by name.</td>
</tr>
<tr>
<td>Certificates and the directories</td>
<td>They are files and paths on one machine's disk.</td>
</tr>
<tr>
<td>The journaling rules</td>
<td>An address chosen for one server is almost never the right one for another. <code>hmctl journalrule</code> edits them.</td>
</tr>
<tr>
<td>What lives under an account</td>
<td>Application passwords, fetch accounts and folder permissions are the account holder's.</td>
</tr>
<tr>
<td>The selector and key files of a domain whose DKIM key rotation is on</td>
<td>The rotation moves them on its own schedule.</td>
</tr>
<tr>
<td>Settings the environment manages</td>
<td>In a container the value is the deployment's. The server refuses a change to such a setting with <code>409</code>.</td>
</tr>
<tr>
<td>Remote domain policies and alert rules</td>
<td>The wiki's own addition: they have routes of their own that <code>config</code> does not read.</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>What this server has changed from stock.</strong> <code>hmctl config changed</code>, or <code>Get-HmChangedSetting</code> in PowerShell, lists every setting whose value is not the one this version ships with, with the shipped value beside it. <a href="/topic/28222">Settings: what is not stock</a> has the route behind it, the other places to read the same list, what "default" means and who may see it.</p>
<pre><code class="language-text">$ 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.
</code></pre>
<p dir="auto">A secret is named and never shown. A value that the file's <code>[SettingsOverride]</code> section is forcing says <code>(forced from [SettingsOverride])</code>, a value the environment sets says <code>(set by the environment)</code>, and a name the server holds a value for and ships no default for says <code>(none: this server does not read it)</code>. A difference is not an exit code here, unlike <code>config diff</code>, because every real server has changed something.</p>
<h3><a class="anchor-offset" name="reports-and-country-blocking-from-a-shell"></a>Reports and country blocking from a shell</h3>
<pre><code class="language-bash">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 -
</code></pre>
<p dir="auto"><code>--csv</code> fetches the server's own CSV with <code>format=csv</code> instead of building one from the JSON. <a href="/topic/28273">Per-domain reports</a> has the sections, their sources and parameters, and who may read them.</p>
<p dir="auto"><strong>Run <code>hmctl report sections</code> first.</strong> 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 <code>--csv</code>, the section's own note is printed above the table.</p>
<p dir="auto"><a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/CountryBlocking.md" rel="nofollow ugc">CountryBlocking.md</a> shows the country policy, which <a href="/topic/28249">the topic on allow and block lists and country blocking</a> explains, set from both clients:</p>
<pre><code class="language-bash">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
</code></pre>
<pre><code class="language-powershell">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' }
</code></pre>
<p dir="auto"><code>geoip_database_status</code> 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.</p>
<h3><a class="anchor-offset" name="what-neither-client-does"></a>What neither client does</h3>
<ul>
<li><strong>Neither reads or writes the message store.</strong> Mail is IMAP's business.</li>
<li><strong>Neither is a backup tool of its own.</strong> <code>hmctl backup start</code>, <code>hmctl backup restore</code> and <code>hmctl backup passphrase</code> ask the server; the archive is written, read and restored by the server.</li>
<li><strong>Neither sets the administrator password.</strong> <code>hmailserver --set-admin-password</code> on the server does that, on Windows <code>hMailServer.exe --set-admin-password</code>, with the password in <code>HM_ADMIN_PASSWORD</code> or on standard input and never as an argument. On Windows the Control Panel does it too, but only where the service may write <code>hMailServer.ini</code>, as under LocalSystem: under <code>NT SERVICE\hMailServer</code>, a new 6.3.4 installation's account, its <em>Administrator password</em> card is refused with a sentence.</li>
<li><strong>Neither makes or carries the stored-secrets key.</strong> That is done on the server itself, never over the API.</li>
<li><strong>Neither can turn the REST listener on</strong>, since both need it.</li>
<li><strong>Neither sets what the environment sets</strong> in a container. No container image of 6.3.4 is published.</li>
<li><strong>Neither invents a field.</strong> A field that was not given is one the server decides.</li>
<li><strong><code>hmctl</code> has no pending verb.</strong> <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SettingsApply.md" rel="nofollow ugc">SettingsApply.md</a> notes that <code>GET /api/v1/server/settings/pending</code> is one request; <code>hmctl api</code> takes a method and a path.</li>
</ul>
<h3><a class="anchor-offset" name="where-to-read-more"></a>Where to read more</h3>
<ul>
<li>The operator document: <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/CommandLine.md" rel="nofollow ugc">CommandLine.md</a>, with a row for every resource and sections on DKIM rotation, bounces, complaints, DMARC reports, OpenPGP keys, the mailbox move and setting profiles.</li>
<li>The wiki page: <a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/Command-Line" rel="nofollow ugc">Command Line</a>.</li>
<li>For the examples above: <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/ChangedFromStock.md" rel="nofollow ugc">ChangedFromStock.md</a>, <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Reports.md" rel="nofollow ugc">Reports.md</a> and <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/CountryBlocking.md" rel="nofollow ugc">CountryBlocking.md</a>.</li>
<li>The public documentation: <a href="https://www.progressiverobot.com/hmailserver-documentation/#24-the-rest-api" rel="nofollow ugc">chapter 24, The REST API</a>, which both clients speak, and <a href="https://www.progressiverobot.com/hmailserver-documentation/#3510-administering-a-linux-server" rel="nofollow ugc">35.10, Administering a Linux server</a>.</li>
<li>On this forum: the <a href="/topic/28189">Linux guide</a>, <a href="/topic/28192">The Control Deck</a>, the <a href="/topic/28218">6.3.4 announcement</a>, <a href="/topic/28264">the REST API guide</a> and, for what <code>hmctl webhook</code> manages, <a href="/topic/28266">the send API and webhooks</a>.</li>
</ul>
]]></description><link>https://www.hmailserver.co.uk/topic/28265/hmctl-and-the-powershell-module-shell-administration-and-accounts-from-a-csv-file</link><generator>RSS for Node</generator><lastBuildDate>Fri, 06 Nov 2026 09:05:50 GMT</lastBuildDate><atom:link href="https://www.hmailserver.co.uk/topic/28265.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 27 Sep 2026 19:47:13 GMT</pubDate><ttl>60</ttl></channel></rss>