<?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[Administrator roles end to end: the 27 permissions, giving a role, where each works]]></title><description><![CDATA[<p dir="auto"><em>Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) is a security release that closes gaps in what this guide describes. Over COM an account given <code>serveradmin</code> with a list of domains was the server's administrator, its list aside, and one given <code>domainadmin</code> with a list that left out its own domain administered that domain's ordinary accounts; both are now confined as over REST, so before upgrading give <code>serveradmin</code> with no list to any account that is to administer the whole server over COM, a script's or a Control Panel's account among them. A credential confined to named domains is refused a domain's rename, a domain alias, a custom role's creation, change or deletion, a rule action that reaches the server's own addresses, a route's stored credentials or the event script, a relay host that is not public, and the server-wide metric history, over REST and COM alike; and a role is given or taken away only by a credential that takes in what the account holds now and afterwards, so <code>manage_roles</code> in named domains can no longer take a role from an account whose admin level is the server administrator's. A state-changing REST request that a browser reports as cross-site is refused. In the Control Panel, <em>API &amp; monitoring</em> is <em>Monitoring &amp; alerts</em> and <em>REST API keys</em> is under <em>Sign-in &amp; access</em>; Ctrl+K takes every old title to its page. See <a href="/topic/28277">the 6.3.7 announcement</a>.</em></p>
<p dir="auto">This guide puts administrator roles in one place. Roles are new since 6.3.4 (schema 6047). <strong>The one thing to know first: an upgrade changes nobody's authority.</strong> The upgrade writes no assignment, and an account with no assignment holds exactly what its admin level has always given it. Nothing narrows until you give somebody a role.</p>
<h3><a class="anchor-offset" name="permissions-roles-and-assignments"></a>Permissions, roles and assignments</h3>
<p dir="auto">A <strong>permission</strong> is one thing a credential may do, a <strong>role</strong> is a named set of permissions, and an <strong>assignment</strong> gives one account one role in named domains. An account holds at most one role.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Role</th>
<th>For</th>
<th>Reaches when no domain is named</th>
<th>The same as</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>serveradmin</code></td>
<td>Everything, including the secrets no other role reads</td>
<td>Every domain</td>
<td>The administrator password, admin level <em>Server</em>, an API key with <code>Scope=full</code> and no role</td>
</tr>
<tr>
<td><code>domainadmin</code></td>
<td>The accounts, aliases, lists and mailboxes of a domain</td>
<td>The account's own domain</td>
<td>Admin level <em>Domain</em></td>
</tr>
<tr>
<td><code>helpdesk</code></td>
<td>Finding an account, resetting its password, unlocking it, and reading where its mail went, and nothing else</td>
<td>The account's own domain</td>
<td>Nothing before 6.3.4</td>
</tr>
<tr>
<td><code>auditor</code></td>
<td>Reading everything the administration exposes, the audit trail included, and changing nothing</td>
<td>Every domain</td>
<td>An API key with <code>Scope=readonly</code> and no role</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>The built-in roles cannot be edited or deleted.</strong> Their permissions are in the server's code, not the <code>hm_roles</code> table, so a change to the table cannot hand anybody a permission.</p>
<h3><a class="anchor-offset" name="the-27-permissions-and-who-holds-them"></a>The 27 permissions and who holds them</h3>
<p dir="auto">A permission marked <em>server-wide</em> is reached only by a credential with no list of domains. The queue, the quarantine, the archive, the logs and the settings carry every domain's mail, so a role confined to named domains is refused them whatever it holds.</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Permission</th>
<th>What it reaches</th>
<th><code>serveradmin</code></th>
<th><code>domainadmin</code></th>
<th><code>helpdesk</code></th>
<th><code>auditor</code></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>administer_server</code></td>
<td>Everything, secrets such as password hashes and private keys included; COM's "is this the server administrator?"</td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>administer_domain</code></td>
<td>COM's "is this a domain administrator?"</td>
<td>yes</td>
<td>yes</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>view_configuration</code></td>
<td>Status, settings, listeners, certificates, rules, routes, anti-spam lists, the roles and who holds them, alert rules and events, bounce and complaint readings; never a secret</td>
<td>yes</td>
<td>yes</td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>view_accounts</code></td>
<td>Domains, accounts, aliases, lists, groups <em>(server-wide)</em>, fetch accounts</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td><code>view_mailbox</code></td>
<td>Another account's folders and messages</td>
<td>yes</td>
<td>yes</td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>view_queue</code></td>
<td>The delivery queue <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>view_quarantine</code></td>
<td>The quarantine <em>(server-wide)</em></td>
<td>yes</td>
<td>yes</td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>view_archive</code></td>
<td>The archive <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>view_logs</code></td>
<td>The logs <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>view_message_trace</code></td>
<td>The message trace; with <code>view_configuration</code>, the reports and a domain's suppression list</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td><code>view_audit</code></td>
<td>The audit trail <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>manage_accounts</code></td>
<td>Creating, changing and deleting accounts, aliases, lists, groups, fetch accounts and app passwords; a domain's spam traps, DKIM rotation and suppression list; moving a mailbox (needs both domains)</td>
<td>yes</td>
<td>yes</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>reset_password</code></td>
<td><code>POST /api/v1/accounts/{address}/password</code>, <code>Application.ResetPassword</code></td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td></td>
</tr>
<tr>
<td><code>unlock_account</code></td>
<td><code>POST /api/v1/accounts/{address}/unlock</code>, <code>Application.UnlockAccount</code>, <code>Account.UnlockMailbox</code>; clearing the logon-failure list <em>(server-wide)</em></td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td></td>
</tr>
<tr>
<td><code>manage_mailbox</code></td>
<td>Another account's folders, messages and folder rights, a support session, a PST migration job (with <code>manage_accounts</code>)</td>
<td>yes</td>
<td>yes</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_queue</code></td>
<td>Retrying or deleting a queued message <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_quarantine</code></td>
<td>Releasing or deleting held mail <em>(server-wide)</em></td>
<td>yes</td>
<td>yes</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_archive</code></td>
<td>Holding and releasing archived messages <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_settings</code></td>
<td>Every setting, listener, certificate, route, rule and anti-spam list, the alert rules <em>(server-wide)</em>; the webmail's branding and file-link policy, a confined credential only its own domains'</td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_domains</code></td>
<td>Which domains exist <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_server</code></td>
<td>Reinitialise, back up, reload scripts, clear caches, install an update, run a pass now <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_roles</code></td>
<td>Roles and who holds them <em>(server-wide)</em></td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>send_through_api</code></td>
<td>Sending through <code>POST /api/v1/send</code>; only an API key reaches it</td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>provision</code></td>
<td>SCIM 2.0 at <code>/scim/v2</code>, reads and writes; only an API key reaches it</td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>view_discovery</code></td>
<td>eDiscovery's cases, searches, holds and exports</td>
<td>yes</td>
<td></td>
<td></td>
<td>yes</td>
</tr>
<tr>
<td><code>manage_discovery</code></td>
<td>Opening and closing a case, searching, legal holds, exports</td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>manage_inbound_webhooks</code></td>
<td>Routing an address or a domain to a URL, as much as reading every message the route takes</td>
<td>yes</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p dir="auto">Four rules sit beside the table:</p>
<ul>
<li><strong>The auditor reads mail.</strong> It holds <code>view_mailbox</code>; the help desk does not.</li>
<li><strong>The reports need two permissions.</strong> <code>GET /api/v1/reports</code> needs <code>view_configuration</code> and <code>view_message_trace</code>, because it says who mails whom. <code>domainadmin</code> and <code>auditor</code> read them; <code>helpdesk</code> is refused, and the Deck leaves the <em>Reports</em> view out for any role without both.</li>
<li><strong>The trace follows the role's reach.</strong> A confined help desk traces one whole address in a domain it reaches and sees only events naming that address exactly: <code>user@example.test</code> does not also show <code>otheruser@example.test</code>.</li>
<li><strong>Clearing the logon-failure list is server-wide.</strong> <code>hm_logon_failures</code> records only the address a failure came from, with no account or domain, so a confined help desk is refused <code>POST /api/v1/settings/logon-failures/clear</code>, and <code>Settings.ClearLogonFailureList</code> answers the server administrator alone. The help desk unlocks one account with <code>POST /api/v1/accounts/{address}/unlock</code> instead; a password reset clears the lockout too. A ban already placed is an IP range under <code>/api/v1/ipranges</code>, which needs <code>manage_settings</code>.</li>
</ul>
<h3><a class="anchor-offset" name="what-only-serveradmin-has"></a>What only serveradmin has</h3>
<p dir="auto"><strong>Reading a secret.</strong> No <code>view_</code> permission covers a password hash or a private key: an auditor that could read one would be an offline attack away from being every role.</p>
<p dir="auto"><strong>Handing out authority.</strong> No custom role may hold <code>administer_server</code>, <code>administer_domain</code> or <code>manage_roles</code>, and a credential may never give a role holding a permission, or reaching a domain, that it does not hold itself.</p>
<p dir="auto"><strong>Taking over a wider account.</strong> A credential may not reset the password of, or change, an account whose whole authority it does not take in. A help desk is refused a domain administrator's or an auditor's password, even in its own domain, because otherwise it could sign in as them; a domain administrator is refused an auditor's. Where the server administrator's own mailbox is in a domain somebody else administers, that domain's administrator is refused all of these on it, over REST and COM. Over COM this is asked where the write is made, not where the object was reached: <code>Account.Save</code>, <code>Account.Delete</code>, <code>Account.MoveTo</code>, <code>Application.ResetPassword</code>, the app passwords and every write to what is in an account (<code>FetchAccount.Save</code>, <code>Rule.Save</code>, <code>IMAPFolder.Save</code>, <code>Message.Save</code> and the rest) 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. <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Roles.md" rel="nofollow ugc">Roles.md</a> lists every route and COM member that asks this.</p>
<h3><a class="anchor-offset" name="which-domains-a-role-reaches"></a>Which domains a role reaches</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Credential</th>
<th>No domains named</th>
<th><code>*</code></th>
<th>A list</th>
</tr>
</thead>
<tbody>
<tr>
<td>An assignment of <code>serveradmin</code> or <code>auditor</code></td>
<td>Every domain</td>
<td>Every domain</td>
<td>Only those</td>
</tr>
<tr>
<td>An assignment of any other role, custom ones included</td>
<td>The account's own domain</td>
<td>Every domain</td>
<td>Only those</td>
</tr>
<tr>
<td>An API key's <code>Domains=</code> line, whatever its role</td>
<td>Every domain</td>
<td>Matches no domain</td>
<td>Only those</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>A custom role holding a server-wide permission needs <code>*</code></strong> before an account can use it at all. The listings answer <code>every_domain</code> over REST, and a fourth field over COM. A credential confined to named domains sees only the assignments of accounts in those domains.</p>
<h3><a class="anchor-offset" name="giving-a-role-surface-by-surface"></a>Giving a role, surface by surface</h3>
<p dir="auto"><strong>Control Panel (Windows only):</strong> <em>Accounts &amp; domains → Administrator roles</em> lists the roles and who holds them. Type an address, choose a role, optionally name the domains, and press <em>Give the role</em>; select a row and press <em>Take the role away</em>. It applies at once.</p>
<p dir="auto"><strong>Control Deck (Windows and Linux):</strong> the <em>Roles</em> view does the same.</p>
<p dir="auto"><strong>REST:</strong> giving or taking away a role needs <code>manage_roles</code>, which only <code>serveradmin</code> holds and which is server-wide: the administrator password, or a <code>serveradmin</code> credential reaching every domain.</p>
<pre><code>PUT    /api/v1/role-assignments/helen@example.test   {"role":"helpdesk","domains":"example.test,example.org"}
DELETE /api/v1/role-assignments/helen@example.test
GET    /api/v1/role-assignments
GET    /api/v1/roles
</code></pre>
<p dir="auto"><strong>COM (Windows only):</strong> an empty role name takes the role away, and <code>Account.RoleName</code>, <code>RoleDomains</code> and <code>RolePermissions</code> read it back:</p>
<pre><code class="language-powershell">$app = New-Object -ComObject 'hMailServer.Application'
$account = $app.Authenticate('Administrator', 'your-admin-password')
if (-not $account) { throw 'Authentication failed.' }
$app.AssignRole('helen@example.test', 'helpdesk', 'example.test')
$helen = $app.Domains.ItemByName('example.test').Accounts.ItemByAddress('helen@example.test')
$helen.RoleName; $helen.RoleDomains; $helen.RolePermissions
</code></pre>
<p dir="auto">With a second factor on the administrator, <code>Authenticate</code> refuses and the script uses <code>AuthenticateWithCode</code>. <code>Application.CurrentRole</code> and <code>CurrentPermissions</code> say what a session holds.</p>
<p dir="auto"><strong>An API key:</strong> <code>POST /api/v1/apikeys</code> with <code>"role":"helpdesk"</code> writes a <code>Role=</code> line to <code>hMailServerApiKeys.ini</code>. The key store answers to the administrator password alone. The Deck's <em>API keys</em> view, signed in with that password, also makes a key with a role and its domains, showing the token once.</p>
<pre><code class="language-bash">curl -u Administrator:your-admin-password \
     -H "Content-Type: application/json" \
     -d '{"label":"help desk tool","role":"helpdesk","domains":"example.test"}' \
     https://&lt;host&gt;:8045/api/v1/apikeys
</code></pre>
<p dir="auto">Use the address and port your listener answers on; bound to loopback with no certificate, it is <code>http://127.0.0.1:8045</code> (<code>https://</code> once a certificate, or an ACME one, is in place). A key's role decides what it reaches. The Control Panel's <em>REST API keys</em> page, as its reference describes it, names no role; give a key its role over REST or in the Deck. <strong>No API key signs in to the Deck.</strong></p>
<p dir="auto"><strong>Custom roles</strong> are made over REST from a name, a description and a comma-separated permission list; both editors list them and give them out. A role that releases held mail holds server-wide permissions, so it is given with <code>*</code> and reaches every domain's held mail:</p>
<pre><code>POST /api/v1/roles     {"name":"quarantine-desk","description":"releases held mail","permissions":"view_quarantine,manage_quarantine"}
PUT  /api/v1/role-assignments/helen@example.test   {"role":"quarantine-desk","domains":"*"}
</code></pre>
<p dir="auto">A permission the server does not know is dropped, not refused, so a custom role ends with less authority than it asked for, never more. <strong>A custom role somebody holds cannot be deleted:</strong> <code>DELETE /api/v1/roles/{name}</code> answers <code>409</code> saying how many accounts hold it, and the Deck's <em>Delete</em> shows that sentence. Give each holder another role or take this one away first; assignments are never removed for you, because an account with none falls back to its admin level. A built-in role answers <code>400</code>, and COM cannot delete a role. <strong>An assignment decides even when its role holds nothing:</strong> holders of a custom role whose permissions name nothing this version knows are ordinary accounts, not their admin level.</p>
<h3><a class="anchor-offset" name="where-each-role-works"></a>Where each role works</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Role</th>
<th>Control Deck</th>
<th>REST API</th>
<th>Control Panel (COM)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>serveradmin</code></td>
<td>Every view</td>
<td>Every route</td>
<td>Every page</td>
</tr>
<tr>
<td><code>domainadmin</code></td>
<td>Dashboard, DANE / TLSA, domains, fetch accounts, lists, roles and reports for its domains</td>
<td>Its domains' accounts, aliases, lists and mailboxes, the status, who holds a role there</td>
<td>Domains and Administrator roles</td>
</tr>
<tr>
<td><code>helpdesk</code></td>
<td>Domains, fetch accounts and lists, with <em>Reset password</em> and <em>Unlock</em> on every account</td>
<td>The reset, the unlock, the account listings, the trace of an address in its domains</td>
<td>Welcome and About only; <code>Application.ResetPassword</code>, <code>UnlockAccount</code> and <code>GlobalObjects.MessageTrace</code> answer a script</td>
</tr>
<tr>
<td><code>auditor</code></td>
<td>Every view that reads, the audit trail and reports included</td>
<td>Every read, the audit trail and alerts included; every write refused</td>
<td>Welcome, About and Administrator roles</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>The help desk and the auditor work in the Deck.</strong> COM's settings objects answer only the server administrator, so an auditor reads the configuration through the Deck or REST. A domain administrator is not shown the settings, rules, routes, remote domains, certificates, ports, IP ranges, groups, backup, server messages, queue, logs or audit trail, which are server-wide. The quarantine is server-wide as well, so a domain administrator confined to its domain is refused it.</p>
<p dir="auto"><strong>Hiding a page is a courtesy, not the boundary.</strong> The server refuses the request whatever a page draws, and a Control Panel connected to a server that predates roles shows every page.</p>
<p dir="auto"><strong>A role holder signs in with its own address and password</strong>, and its second factor where it has one. <strong>An app password never carries the role:</strong> over REST it is an ordinary account's request, and COM's <code>Application.Authenticate</code> and <code>AuthenticateWithCode</code> treat it as a wrong password. A support session into a mailbox does not carry the person's role either. A role holder's own mail and settings under <code>/api/v1/me</code> stay its own.</p>
<h3><a class="anchor-offset" name="setting-up-a-help-desk"></a>Setting up a help desk</h3>
<ol>
<li><strong>Turn the REST listener on</strong> if it is off. It needs <code>RestApiPort</code> set, <code>RestApiBindAddress</code> set to an address staff can reach (the default, <code>127.0.0.1</code>, serves this machine alone), a certificate unless it is bound to loopback, and a name staff can reach. On Windows set them on the Control Panel's <em>API &amp; monitoring</em> page (<em>Monitoring &amp; alerts</em> since 6.3.7) and press <em>Apply now</em>, since they wait for a reinitialise. On Linux run <code>sudo hmailserver --set-setting RestApiPort=8045</code> and the same for <code>RestApiBindAddress</code> and, for any address but loopback, <code>RestApiCertificateFile</code> and <code>RestApiPrivateKeyFile</code> (or let an ACME certificate stand in), then <code>sudo systemctl restart hmailserver</code>. <a href="/topic/28192">The Control Deck</a> topic has the details.</li>
<li><strong>Give each person a mailbox of their own</strong>; the role goes to that account.</li>
<li><strong>Decide the domains.</strong> With none named, <code>helpdesk</code> reaches only the account's own domain; name several, comma separated, for a desk serving more.</li>
<li><strong>Give the role</strong> on any surface above and check it with <code>GET /api/v1/role-assignments</code> or the Roles view.</li>
<li><strong>The person signs in</strong> to the Deck at <code>https://&lt;host&gt;:8045/</code> as themselves, then uses <em>Reset password</em> or <em>Unlock</em> on an account.</li>
<li><strong>Tell them the limits:</strong> no mail reading, no reports, no domain administrator's or auditor's password, no removing a ban, and, when confined, no clearing of the logon-failure list.</li>
</ol>
<p dir="auto">Each change the help desk makes is on the audit trail as its own, over COM and REST, with actor type <code>account</code> and a detail ending <code>[role: helpdesk]</code>.</p>
<h3><a class="anchor-offset" name="what-a-refusal-says"></a>What a refusal says</h3>
<ul>
<li><strong>A permission the role lacks:</strong> <code>403</code> naming both, for example <code>the role 'helpdesk' does not hold the permission 'manage_settings'</code>.</li>
<li><strong>A domain it does not reach:</strong> <code>the role 'helpdesk' does not reach that domain</code>, <strong>whether or not the account exists</strong>, so nobody can list another domain's mailboxes one request at a time. Over COM, <code>Application.ResetPassword</code> and <code>UnlockAccount</code> answer <code>No such account.</code> for both.</li>
<li><strong>An account wider than the credential:</strong> <em>that account holds authority this credential does not</em>.</li>
<li><strong>Over COM,</strong> since 6.3.4 a refusal carries the server's sentence rather than a bare <code>Exception from HRESULT: 0x800403E9</code>.</li>
<li><strong>An API key on the key store, the <code>[Settings]</code> routes, the audit trail or the alerts:</strong> <code>401</code> with <code>{"error":"authentication failed"}</code>, the same as "not a key".</li>
</ul>
<h3><a class="anchor-offset" name="the-two-stores-only-the-administrator-password-opens"></a>The two stores only the administrator password opens</h3>
<p dir="auto">The <strong>key store</strong> (<code>/api/v1/apikeys</code>) and the <strong><code>[Settings]</code> section of <code>hMailServer.ini</code></strong> (<code>/api/v1/settings/ini</code>) answer to the administrator password and nothing else: not an account holding <code>serveradmin</code>, not a key of any role or scope. A key that could mint keys could issue itself an unrestricted replacement, and the file holds the server's secrets. An account is refused <code>403</code> with the reason, a key <code>401</code>.</p>
<p dir="auto">The <strong>audit trail</strong> (<code>/api/v1/audit</code>) and the <strong>alerts</strong> (<code>/api/v1/alerts/...</code>) are refused to every API key with the same <code>401</code>, so a stolen key cannot read what it did or silence the alerting. An account whose role reaches every domain reaches them through <code>view_audit</code>, <code>view_configuration</code>, <code>manage_settings</code> or <code>manage_server</code>; a confined role is refused them.</p>
<h3><a class="anchor-offset" name="upgrades-the-audit-trail-and-backups"></a>Upgrades, the audit trail and backups</h3>
<p dir="auto"><strong>The upgrade.</strong> Schema step 6047 adds <code>hm_roles</code>, seeded with the four built-in rows, and <code>hm_roleassignments</code>, and writes no assignment. Roles are data, not settings: nothing goes into <code>hm_settings</code> or <code>hMailServer.ini</code>.</p>
<p dir="auto"><strong>The audit trail.</strong> Every role created, changed or deleted, and every role given or taken away, is a row naming the credential that did it; an assignment's row reads like <code>role: helpdesk -&gt; auditor; domains: the account's own domain -&gt; every domain</code>. The actor type follows the role: the administrator password and holders of <code>serveradmin</code> or <code>domainadmin</code> are <code>administrator</code>, every other account is <code>account</code>.</p>
<p dir="auto"><strong>Backup and restore.</strong> An account's assignment travels with it, by role name, and the custom roles travel whenever the domains or the settings do, restored first. A role the restoring server already has is kept as it is, with any difference named in the backup log. The admin level waits until the assignment is written, so a restore that stops part-way leaves no account wider than its role. An assignment naming a role neither side has is dropped, the level stays withheld, and <code>HM6129</code> names the account. No archive carries the API key store.</p>
<h3><a class="anchor-offset" name="what-it-does-not-do"></a>What it does not do</h3>
<ul>
<li><strong>No mapping from an identity provider.</strong> There is no claims mapping; an identity provider saying somebody is an administrator is not obeyed. The server's roles and <code>oidc_administrators</code> or <code>saml_administrators</code> decide.</li>
<li><strong>No role on an app password or a support session.</strong></li>
<li><strong>No Control Panel or COM on Linux:</strong> roles are given there in the Deck and over REST.</li>
</ul>
<h3><a class="anchor-offset" name="where-to-read-more"></a>Where to read more</h3>
<ul>
<li><a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Roles.md" rel="nofollow ugc">Roles.md</a>, the authority on all of this.</li>
<li><a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/Sign-in-and-Identity" rel="nofollow ugc">Sign-in and Identity</a> §7, <a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/The-Control-Deck" rel="nofollow ugc">The Control Deck</a>, <a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/The-REST-API" rel="nofollow ugc">The REST API</a> §2 and <a href="https://gitlab.com/hmailserver/hmailserver/-/wikis/The-COM-API-and-Scripting" rel="nofollow ugc">The COM API and Scripting</a> §23.2.</li>
<li><a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/AlertsAndAuditTrail.md" rel="nofollow ugc">AlertsAndAuditTrail.md</a>, <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Backup.md" rel="nofollow ugc">Backup.md</a> and <a href="https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Reports.md" rel="nofollow ugc">Reports.md</a>.</li>
<li>On this forum: <a href="/topic/28192">The Control Deck: browser administration at /</a>, <a href="/topic/28264">The REST API: turning it on, credentials and roles</a>, <a href="/topic/28262">The COM API on Windows</a>, <a href="/topic/28241">Hardening a public mail server</a>, <a href="/topic/28273">Alerts and the audit trail</a> and <a href="/topic/28239">IP ranges, auto-ban and lockout</a>.</li>
</ul>
]]></description><link>https://www.hmailserver.co.uk/topic/28237/administrator-roles-end-to-end-the-27-permissions-giving-a-role-where-each-works</link><generator>RSS for Node</generator><lastBuildDate>Fri, 06 Nov 2026 09:05:49 GMT</lastBuildDate><atom:link href="https://www.hmailserver.co.uk/topic/28237.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 27 Sep 2026 19:19:13 GMT</pubDate><ttl>60</ttl></channel></rss>