Skip to content
  • Categories
  • Recent
  • Popular
  • Tags
  • Users
  • Groups
  • Documentation
  • Downloads
  • Report a bug
  • GitHub mirror
  • Paid support
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
hMailServer Community Forum
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ
  1. Support and discussion for hMailServer
  2. Running hMailServer
  3. Guides & tutorials
  4. Administrator roles end to end: the 27 permissions, giving a role, where each works

Administrator roles end to end: the 27 permissions, giving a role, where each works

Scheduled Pinned Locked Moved Guides & tutorials
administrationsecuritycontrol-deckrest-apicontrol-panel
1 Posts 1 Posters 154 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ProgressiverobotP
    ProgressiverobotP
    Progressiverobot
    hMailServer Team
    wrote on last edited by chris
    #1

    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 serveradmin with a list of domains was the server's administrator, its list aside, and one given domainadmin 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 serveradmin 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 manage_roles 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, API & monitoring is Monitoring & alerts and REST API keys is under Sign-in & access; Ctrl+K takes every old title to its page. See the 6.3.7 announcement.

    This guide puts administrator roles in one place. Roles are new since 6.3.4 (schema 6047). The one thing to know first: an upgrade changes nobody's authority. 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.

    Permissions, roles and assignments

    A permission is one thing a credential may do, a role is a named set of permissions, and an assignment gives one account one role in named domains. An account holds at most one role.

    Role For Reaches when no domain is named The same as
    serveradmin Everything, including the secrets no other role reads Every domain The administrator password, admin level Server, an API key with Scope=full and no role
    domainadmin The accounts, aliases, lists and mailboxes of a domain The account's own domain Admin level Domain
    helpdesk Finding an account, resetting its password, unlocking it, and reading where its mail went, and nothing else The account's own domain Nothing before 6.3.4
    auditor Reading everything the administration exposes, the audit trail included, and changing nothing Every domain An API key with Scope=readonly and no role

    The built-in roles cannot be edited or deleted. Their permissions are in the server's code, not the hm_roles table, so a change to the table cannot hand anybody a permission.

    The 27 permissions and who holds them

    A permission marked server-wide 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.

    Permission What it reaches serveradmin domainadmin helpdesk auditor
    administer_server Everything, secrets such as password hashes and private keys included; COM's "is this the server administrator?" yes
    administer_domain COM's "is this a domain administrator?" yes yes
    view_configuration 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 yes yes yes
    view_accounts Domains, accounts, aliases, lists, groups (server-wide), fetch accounts yes yes yes yes
    view_mailbox Another account's folders and messages yes yes yes
    view_queue The delivery queue (server-wide) yes yes
    view_quarantine The quarantine (server-wide) yes yes yes
    view_archive The archive (server-wide) yes yes
    view_logs The logs (server-wide) yes yes
    view_message_trace The message trace; with view_configuration, the reports and a domain's suppression list yes yes yes yes
    view_audit The audit trail (server-wide) yes yes
    manage_accounts 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) yes yes
    reset_password POST /api/v1/accounts/{address}/password, Application.ResetPassword yes yes yes
    unlock_account POST /api/v1/accounts/{address}/unlock, Application.UnlockAccount, Account.UnlockMailbox; clearing the logon-failure list (server-wide) yes yes yes
    manage_mailbox Another account's folders, messages and folder rights, a support session, a PST migration job (with manage_accounts) yes yes
    manage_queue Retrying or deleting a queued message (server-wide) yes
    manage_quarantine Releasing or deleting held mail (server-wide) yes yes
    manage_archive Holding and releasing archived messages (server-wide) yes
    manage_settings Every setting, listener, certificate, route, rule and anti-spam list, the alert rules (server-wide); the webmail's branding and file-link policy, a confined credential only its own domains' yes
    manage_domains Which domains exist (server-wide) yes
    manage_server Reinitialise, back up, reload scripts, clear caches, install an update, run a pass now (server-wide) yes
    manage_roles Roles and who holds them (server-wide) yes
    send_through_api Sending through POST /api/v1/send; only an API key reaches it yes
    provision SCIM 2.0 at /scim/v2, reads and writes; only an API key reaches it yes
    view_discovery eDiscovery's cases, searches, holds and exports yes yes
    manage_discovery Opening and closing a case, searching, legal holds, exports yes
    manage_inbound_webhooks Routing an address or a domain to a URL, as much as reading every message the route takes yes

    Four rules sit beside the table:

    • The auditor reads mail. It holds view_mailbox; the help desk does not.
    • The reports need two permissions. GET /api/v1/reports needs view_configuration and view_message_trace, because it says who mails whom. domainadmin and auditor read them; helpdesk is refused, and the Deck leaves the Reports view out for any role without both.
    • The trace follows the role's reach. A confined help desk traces one whole address in a domain it reaches and sees only events naming that address exactly: [email protected] does not also show [email protected].
    • Clearing the logon-failure list is server-wide. hm_logon_failures records only the address a failure came from, with no account or domain, so a confined help desk is refused POST /api/v1/settings/logon-failures/clear, and Settings.ClearLogonFailureList answers the server administrator alone. The help desk unlocks one account with POST /api/v1/accounts/{address}/unlock instead; a password reset clears the lockout too. A ban already placed is an IP range under /api/v1/ipranges, which needs manage_settings.

    What only serveradmin has

    Reading a secret. No view_ 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.

    Handing out authority. No custom role may hold administer_server, administer_domain or manage_roles, and a credential may never give a role holding a permission, or reaching a domain, that it does not hold itself.

    Taking over a wider account. 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: Account.Save, Account.Delete, Account.MoveTo, Application.ResetPassword, the app passwords and every write to what is in an account (FetchAccount.Save, Rule.Save, IMAPFolder.Save, Message.Save 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. Roles.md lists every route and COM member that asks this.

    Which domains a role reaches

    Credential No domains named * A list
    An assignment of serveradmin or auditor Every domain Every domain Only those
    An assignment of any other role, custom ones included The account's own domain Every domain Only those
    An API key's Domains= line, whatever its role Every domain Matches no domain Only those

    A custom role holding a server-wide permission needs * before an account can use it at all. The listings answer every_domain over REST, and a fourth field over COM. A credential confined to named domains sees only the assignments of accounts in those domains.

    Giving a role, surface by surface

    Control Panel (Windows only): Accounts & domains → Administrator roles lists the roles and who holds them. Type an address, choose a role, optionally name the domains, and press Give the role; select a row and press Take the role away. It applies at once.

    Control Deck (Windows and Linux): the Roles view does the same.

    REST: giving or taking away a role needs manage_roles, which only serveradmin holds and which is server-wide: the administrator password, or a serveradmin credential reaching every domain.

    PUT    /api/v1/role-assignments/[email protected]   {"role":"helpdesk","domains":"example.test,example.org"}
    DELETE /api/v1/role-assignments/[email protected]
    GET    /api/v1/role-assignments
    GET    /api/v1/roles
    

    COM (Windows only): an empty role name takes the role away, and Account.RoleName, RoleDomains and RolePermissions read it back:

    $app = New-Object -ComObject 'hMailServer.Application'
    $account = $app.Authenticate('Administrator', 'your-admin-password')
    if (-not $account) { throw 'Authentication failed.' }
    $app.AssignRole('[email protected]', 'helpdesk', 'example.test')
    $helen = $app.Domains.ItemByName('example.test').Accounts.ItemByAddress('[email protected]')
    $helen.RoleName; $helen.RoleDomains; $helen.RolePermissions
    

    With a second factor on the administrator, Authenticate refuses and the script uses AuthenticateWithCode. Application.CurrentRole and CurrentPermissions say what a session holds.

    An API key: POST /api/v1/apikeys with "role":"helpdesk" writes a Role= line to hMailServerApiKeys.ini. The key store answers to the administrator password alone. The Deck's API keys view, signed in with that password, also makes a key with a role and its domains, showing the token once.

    curl -u Administrator:your-admin-password \
         -H "Content-Type: application/json" \
         -d '{"label":"help desk tool","role":"helpdesk","domains":"example.test"}' \
         https://<host>:8045/api/v1/apikeys
    

    Use the address and port your listener answers on; bound to loopback with no certificate, it is http://127.0.0.1:8045 (https:// once a certificate, or an ACME one, is in place). A key's role decides what it reaches. The Control Panel's REST API keys page, as its reference describes it, names no role; give a key its role over REST or in the Deck. No API key signs in to the Deck.

    Custom roles 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 * and reaches every domain's held mail:

    POST /api/v1/roles     {"name":"quarantine-desk","description":"releases held mail","permissions":"view_quarantine,manage_quarantine"}
    PUT  /api/v1/role-assignments/[email protected]   {"role":"quarantine-desk","domains":"*"}
    

    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. A custom role somebody holds cannot be deleted: DELETE /api/v1/roles/{name} answers 409 saying how many accounts hold it, and the Deck's Delete 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 400, and COM cannot delete a role. An assignment decides even when its role holds nothing: holders of a custom role whose permissions name nothing this version knows are ordinary accounts, not their admin level.

    Where each role works

    Role Control Deck REST API Control Panel (COM)
    serveradmin Every view Every route Every page
    domainadmin Dashboard, DANE / TLSA, domains, fetch accounts, lists, roles and reports for its domains Its domains' accounts, aliases, lists and mailboxes, the status, who holds a role there Domains and Administrator roles
    helpdesk Domains, fetch accounts and lists, with Reset password and Unlock on every account The reset, the unlock, the account listings, the trace of an address in its domains Welcome and About only; Application.ResetPassword, UnlockAccount and GlobalObjects.MessageTrace answer a script
    auditor Every view that reads, the audit trail and reports included Every read, the audit trail and alerts included; every write refused Welcome, About and Administrator roles

    The help desk and the auditor work in the Deck. 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.

    Hiding a page is a courtesy, not the boundary. The server refuses the request whatever a page draws, and a Control Panel connected to a server that predates roles shows every page.

    A role holder signs in with its own address and password, and its second factor where it has one. An app password never carries the role: over REST it is an ordinary account's request, and COM's Application.Authenticate and AuthenticateWithCode 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 /api/v1/me stay its own.

    Setting up a help desk

    1. Turn the REST listener on if it is off. It needs RestApiPort set, RestApiBindAddress set to an address staff can reach (the default, 127.0.0.1, 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 API & monitoring page (Monitoring & alerts since 6.3.7) and press Apply now, since they wait for a reinitialise. On Linux run sudo hmailserver --set-setting RestApiPort=8045 and the same for RestApiBindAddress and, for any address but loopback, RestApiCertificateFile and RestApiPrivateKeyFile (or let an ACME certificate stand in), then sudo systemctl restart hmailserver. The Control Deck topic has the details.
    2. Give each person a mailbox of their own; the role goes to that account.
    3. Decide the domains. With none named, helpdesk reaches only the account's own domain; name several, comma separated, for a desk serving more.
    4. Give the role on any surface above and check it with GET /api/v1/role-assignments or the Roles view.
    5. The person signs in to the Deck at https://<host>:8045/ as themselves, then uses Reset password or Unlock on an account.
    6. Tell them the limits: 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.

    Each change the help desk makes is on the audit trail as its own, over COM and REST, with actor type account and a detail ending [role: helpdesk].

    What a refusal says

    • A permission the role lacks: 403 naming both, for example the role 'helpdesk' does not hold the permission 'manage_settings'.
    • A domain it does not reach: the role 'helpdesk' does not reach that domain, whether or not the account exists, so nobody can list another domain's mailboxes one request at a time. Over COM, Application.ResetPassword and UnlockAccount answer No such account. for both.
    • An account wider than the credential: that account holds authority this credential does not.
    • Over COM, since 6.3.4 a refusal carries the server's sentence rather than a bare Exception from HRESULT: 0x800403E9.
    • An API key on the key store, the [Settings] routes, the audit trail or the alerts: 401 with {"error":"authentication failed"}, the same as "not a key".

    The two stores only the administrator password opens

    The key store (/api/v1/apikeys) and the [Settings] section of hMailServer.ini (/api/v1/settings/ini) answer to the administrator password and nothing else: not an account holding serveradmin, 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 403 with the reason, a key 401.

    The audit trail (/api/v1/audit) and the alerts (/api/v1/alerts/...) are refused to every API key with the same 401, so a stolen key cannot read what it did or silence the alerting. An account whose role reaches every domain reaches them through view_audit, view_configuration, manage_settings or manage_server; a confined role is refused them.

    Upgrades, the audit trail and backups

    The upgrade. Schema step 6047 adds hm_roles, seeded with the four built-in rows, and hm_roleassignments, and writes no assignment. Roles are data, not settings: nothing goes into hm_settings or hMailServer.ini.

    The audit trail. 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 role: helpdesk -> auditor; domains: the account's own domain -> every domain. The actor type follows the role: the administrator password and holders of serveradmin or domainadmin are administrator, every other account is account.

    Backup and restore. 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 HM6129 names the account. No archive carries the API key store.

    What it does not do

    • No mapping from an identity provider. There is no claims mapping; an identity provider saying somebody is an administrator is not obeyed. The server's roles and oidc_administrators or saml_administrators decide.
    • No role on an app password or a support session.
    • No Control Panel or COM on Linux: roles are given there in the Deck and over REST.

    Where to read more

    • Roles.md, the authority on all of this.
    • Sign-in and Identity §7, The Control Deck, The REST API §2 and The COM API and Scripting §23.2.
    • AlertsAndAuditTrail.md, Backup.md and Reports.md.
    • On this forum: The Control Deck: browser administration at /, The REST API: turning it on, credentials and roles, The COM API on Windows, Hardening a public mail server, Alerts and the audit trail and IP ranges, auto-ban and lockout.
    1 Reply Last reply
    0

    Hello! It looks like you're interested in this conversation, but you don't have an account yet.

    Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

    With your input, this post could be even better 💗

    Register Login
    Reply
    • Reply as topic
    Log in to reply
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes


    hMailServer Community

    The community support forum for hMailServer — the free, open-source mail server for Windows and Linux — maintained by Progressive Robot Ltd. The software is AGPL-3.0 and stays that way; this is where it is discussed, debugged and documented.

    The software

    • Documentation
    • Downloads
    • hMailServer 6.3
    • Source on GitLab

    Getting help

    • Forum rules
    • About this forum
    • Support plans
    • Pay monthly support
    • Talk to Progressive Robot

    Legal

    • Privacy policy
    • Terms and conditions
    • Cookie policy

    Progressive Robot Ltd · 220 Chester Business Park, Heronsway, Chester, Cheshire CH4 9GB, United Kingdom · +44 (0)1244 911212 · [email protected] · VAT 506152326

    Powered by NodeBB

    • Login

    • Don't have an account? Register

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Popular
    • Tags
    • Users
    • Groups
    • Documentation
    • Downloads
    • Report a bug
    • GitHub mirror
    • Paid support