# Mailboxes

# Mailboxes

Admin path: **Email Server > Mailboxes** (`view_mailboxes.cfm`,
`add_mailbox.cfm`, `inc/add_mailbox_action.cfm`, `inc/edit_mailbox_action.cfm`,
`inc/edit_mailbox_encryption_action.cfm`, `inc/edit_mailbox_access_control_action.cfm`,
`inc/delete_mailbox_action.cfm`, `inc/get_mailbox_json.cfm`,
`inc/ldap_add_user_mailbox.cfm`, `inc/ldap_add_user_mailbox_remoteauth.cfm`,
`inc/ldap_add_user_groups_mailbox.cfm`, `inc/ldap_delete_user_mailbox.cfm`,
`inc/nextcloud_provision_user.cfm`, `inc/signature_regen_map.cfm`,
`inc/send_mailbox_welcome_email.cfm`, `inc/send_mailbox_welcome_email_remoteauth.cfm`,
`inc/admin_resend_mobile_setup_action.cfm`, `inc/rotate_nc_password_action.cfm`).

This page manages **individual mailboxes** inside the mail-server
topology — one row per address in the `mailboxes` table, joined to a
`recipients` row that carries the per-recipient policy stack (SVF
policy, encryption flags, S/MIME certs, PGP keyrings, 2FA enforcement,
auth type). A mailbox is the local-delivery counterpart to a Relay
Recipient — same `recipients` row shape, different `recipient_type`
column value (`'mailbox'` vs `'relay'`) and a sibling row in
`mailboxes` that gives Dovecot a userdb entry.

This is the **per-mailbox** half of the mail-server topology. Pairs
with [Domains](https://docs.deeztek.com/books/administrator-guide/page/domains-i8v) (the domains those mailboxes live under
and inherit defaults from), [Settings](https://docs.deeztek.com/books/administrator-guide/page/settings) (global Dovecot
config and quota warning thresholds), and the per-address feature
pages: [Aliases](https://docs.deeztek.com/books/administrator-guide/page/aliases), [Shared Mailboxes](https://docs.deeztek.com/books/administrator-guide/page/shared-mailboxes),
[Mailbox Rules](https://docs.deeztek.com/books/administrator-guide/page/mailbox-rules), and per-mailbox app passwords.

## Mailbox vs Alias vs Shared Mailbox vs Relay Recipient

Four address concepts share the namespace under a mailbox domain;
keep them straight:

| Concept | Stored in | Has Dovecot mailbox? | Local sign-in? |
|---|---|---|---|
| **Mailbox** (this page) | `mailboxes` (`mailbox_type='user'`) + `recipients` (`recipient_type='mailbox'`) | Yes — Dovecot LMTP delivery to `/mnt/vmail/<domain>/<user>/` | Yes — IMAP/POP3/Submission, web portal, Nextcloud |
| **Alias** | `mailbox_aliases` | No — forwards to one or more mailboxes (or silently discards) | No |
| **Shared Mailbox** | `mailboxes` (`mailbox_type='shared'`) + `shared_mailbox_permissions` | Yes — but accessed via Dovecot ACL from owner mailboxes | No direct login — owners reach it from their own session |
| **Relay Recipient** | `recipients` (`recipient_type='relay'`) | No — forwarded to a downstream MX | Yes for web portal / Submission (via app passwords) |

See [Aliases](https://docs.deeztek.com/books/administrator-guide/page/aliases) and [Shared Mailboxes](https://docs.deeztek.com/books/administrator-guide/page/shared-mailboxes)
for the alias and shared variants, and
[Email Relay > Relay Recipients](https://docs.deeztek.com/books/administrator-guide/page/relay-recipients)
for the relay-topology equivalent.

## What a Mailbox row carries

```
mailboxes table  (Dovecot userdb-driving row)
├── id, domain_id          -> joins to domains where type='mailbox'
├── username               full email (e.g. jsmith@company.com)
├── name                   display name
├── quota                  per-mailbox quota in BYTES (DB stores bytes;
│                          UI shows GB)
├── active                 1/0 — Dovecot rejects auth when 0
├── nextcloud_enabled      per-mailbox Nextcloud flag
├── mailbox_type           'user' | 'shared'
└── first_name, last_name, title, phone, mobile, department
                           (Pro Personal Information for signature
                            substitution)

recipients table  (paired row, recipient_type='mailbox')
├── recipient              same as mailboxes.username
├── policy_id              -> spam_policies (SVF policy)
├── auth_type              'local' | 'remote'
├── remoteauth_domain      NULL if local; mapping key if remote
├── enforce_mfa            0 | 1 (admin policy)
├── pdf_enabled / smime_enabled / pgp_enabled / digital_sign
└── (cert + keyring slots populated lazily by cert_generation_queue)
```

Side tables linked at create-time or lazily:

| Table | Role |
|---|---|
| `user_settings` | `report_enabled` (quarantine notifications), `train_bayes`, `download_msg`, `timezone`, `ldap_username` |
| `maddr` | Amavis address index — required for the user portal session machinery |
| `sender_login_maps` | Postfix `smtpd_sender_login_maps` entry — permits the mailbox owner to send AS their own address from Submission |
| `app_passwords` | Per-mailbox application passwords (Argon2-hashed) for IMAP/SMTP/CalDAV/CardDAV/Nextcloud. The Add flow creates an initial `Hermes System` app password used by the Nextcloud Mail auto-profile. |
| `recipient_certificates`, `recipient_keystores` | S/MIME cert + PGP keyring slots (lazy — populated by the queue) |
| `cert_generation_queue` | Async S/MIME + PGP generation jobs |
| `mailbox_aliases` | If any aliases exist pointing at the mailbox |
| `shared_mailbox_permissions` | If the mailbox is granted access to any shared mailbox |

## Add Mailbox — `add_mailbox.cfm`

Single-mailbox page (not a bulk form). The admin selects a target
domain, fills in the address local-part + display name + quota + auth
mode + per-recipient stack (policy, notifications, encryption flags),
and submits. `add_mailbox_action.cfm` then runs the full creation
pipeline:

```
form submit  ──► add_mailbox_action.cfm
                     |
                     |  validate domain + email + auth mode
                     |  duplicate-check against recipients, mailboxes,
                     |     mailbox_aliases, virtual_recipients
                     |
                     |  --- write DB ---
                     |  INSERT recipients   (recipient_type='mailbox', policy,
                     |                       auth_type, remoteauth_domain,
                     |                       enforce_mfa, encryption flags)
                     |  INSERT maddr        (Amavis address index)
                     |  INSERT user_settings(notifications, train_bayes,
                     |                       download_msg, timezone)
                     |  INSERT mailboxes    (domain_id, username, name,
                     |                       quota, active=1, nextcloud_enabled)
                     |  INSERT sender_login_maps (permits send-as)
                     |
                     |  --- LDAP ---
                     |  auth_type=local  : ldap_add_user_mailbox.cfm
                     |                     (random userPassword, will be reset)
                     |  auth_type=remote : ldap_add_user_mailbox_remoteauth.cfm
                     |                     (no userPassword; seeAlso pointer to
                     |                     upstream DN, associatedDomain set to
                     |                     remoteauth_domain)
                     |  ldap_add_user_groups_mailbox.cfm
                     |    -> cn=mailboxes,ou=groups,dc=hermes,dc=local
                     |    -> cn=one_factor OR cn=two_factor (per enforce_mfa)
                     |  if NC enabled:
                     |    -> cn=nextcloud,ou=groups,dc=hermes,dc=local
                     |
                     |  --- Nextcloud (if NC enabled) ---
                     |  nextcloud_provision_user.cfm
                     |    -> occ user:add with RANDOM internal password
                     |       (not the user's real password — they reach NC
                     |        via OIDC; the internal password is defense-in-depth)
                     |    -> occ user:setting to pre-fill email + display name
                     |    -> create initial Hermes System app password
                     |       (used by the Mail app account profile)
                     |    -> create Nextcloud Mail account profile
                     |       (IMAP+SMTP credentials pre-wired)
                     |
                     |  --- lazy cert / keyring queue ---
                     |  if smime_enabled : INSERT cert_generation_queue (smime)
                     |  if pgp_enabled   : INSERT cert_generation_queue (pgp)
                     |
                     |  --- send welcome ---
                     |  local  : send_mailbox_welcome_email.cfm
                     |           (password-reset link, 30-min expiry)
                     |  remote : send_mailbox_welcome_email_remoteauth.cfm
                     |           (sign-in with organization password)
                     |
                     |  --- signature map ---
                     |  if Pro: signature_regen_map.cfm
                     |    -> rebuild body milter signature_by_sender map
                     |    -> rebuild sender_data.json
                     |
                     v
            cflocation -> view_mailboxes.cfm with session.m = 1
```

Dovecot mailbox directories on `/mnt/vmail/<domain>/<user>/` are NOT
pre-created. Dovecot auto-creates the directory tree on first LMTP
delivery or first IMAP login. The mailbox row alone is enough.

### Password handling

Local-auth mailboxes:

- The admin enters a password on the Add form (12-char minimum, no
  special chars, checked against the HIBP "Have I Been Pwned" k-anon
  range API).
- The same password is stored in three places, each hashed by its
  consuming subsystem: OpenLDAP `userPassword` (Argon2id via
  `slappasswd -o module-load=argon2.la -h {ARGON2}`), `app_passwords`
  initial `Hermes System` row (Argon2id), and the Nextcloud internal
  user password (only on the NC side, set by `occ user:add` — but
  immediately replaced with a random value by
  `nextcloud_provision_user.cfm`, see Phase 1 of #197).
- Argon2id hashing uses the canonical
  `docker run --rm authelia/authelia:<version> authelia crypto hash generate argon2 --password <value>`
  pattern. No host-side `argon2` binary required.

RemoteAuth mailboxes (`auth_type='remote'`):

- No password is captured. The local LDAP entry has no
  `userPassword`; bind goes through the OpenLDAP remoteauth overlay
  to the upstream AD/LDAP per the `remoteauth_domain` mapping (see
  [LDAP RemoteAuth](https://docs.deeztek.com/books/administrator-guide/page/ldap-remoteauth)).
- `app_passwords` still issues Hermes-side credentials for
  IMAP/SMTP/DAV — these remain Hermes-owned regardless of upstream
  password rotation.

## The Mailboxes table

Single DataTable with 21 columns and an optional Domain filter
dropdown above (populated only when ≥1 domain has mailboxes). Per-row
columns:

| Column | Source | Notes |
|---|---|---|
| Actions | — | Dropdown: Edit Options, Edit Encryption, Reset 2FA Devices, Manage App Passwords (→ `view_mailbox_app_passwords.cfm`), Send Mobile Setup Profile, Rotate NC Internal Password (only if NC enabled), Delete |
| S/MIME | link to `view_recipient_certificates.cfm?type=1&id=...` | Per-mailbox cert manager |
| PGP | link to `view_recipient_keyrings.cfm?type=1&id=...` | Per-mailbox keyring manager |
| Email | `mailboxes.username` | Full address |
| Display Name | `mailboxes.name` | |
| Domain | join on `domains.domain` | |
| Quota | `mailboxes.quota / 1024 / 1024 / 1024` | Rendered in GB |
| Auth | `recipients.auth_type` | `LOCAL` badge or `REMOTE` badge (tooltip shows `remoteauth_domain`) |
| 2FA | LDAP `cn=two_factor` + `enforce_mfa` | Two independent pills — see [Two-pill 2FA column](#two-pill-2fa-column) |
| Policy | `spam_policies.policy_name` | |
| Notifications, Train Bayes, Download Msgs | `user_settings.*` | `YES` (success) / `NO` (secondary) |
| PDF / S/MIME / PGP Encrypt, Sign All | `recipients.*` | `YES` / `NO` |
| S/MIME Cert, PGP Keyring | join against `recipient_certificates`, `recipient_keystores` | `YES` (green) if a cert/keyring exists; spinner badge if a job is `pending`/`processing` in `cert_generation_queue` |
| Nextcloud | `mailboxes.nextcloud_enabled` | `YES` / `NO` |
| Status | `mailboxes.active` | `Active` (success) / `Inactive` (danger) — Dovecot rejects auth when `active=0` |

The query filters `WHERE m.mailbox_type = 'user'` so shared mailboxes
do not appear here — they have their own page at
[Shared Mailboxes](https://docs.deeztek.com/books/administrator-guide/page/shared-mailboxes).

## Two-pill 2FA column

Same two-orthogonal-states model as
[Email Relay > Relay Recipients § Two-pill 2FA column](https://docs.deeztek.com/books/administrator-guide/page/relay-recipients#two-pill-2fa-column).
Admin enforcement (`recipients.enforce_mfa`) and user enrollment
(`cn=two_factor` LDAP membership) are decoupled, so the cell can
show **Enrolled**, **Required**, both, or em-dash.

The page pulls all `cn=two_factor` group members in a single
`ldapsearch` (via `docker exec hermes_ldap ldapsearch -Y EXTERNAL`)
once per render, then each row checks for its DN substring in the
result — avoids an N+1 LDAP roundtrip storm.

## Edit Options modal — AJAX pre-fill

Opens via `loadEditModal(mailboxId)`, hits `inc/get_mailbox_json.cfm`
over AJAX, hydrates every field with the mailbox's current values.
Unlike the Relay Recipients bulk-edit foot-gun, this modal is
**always single-mailbox** — there is no bulk Edit Options on this
page.

Fields:

| Section | Notes |
|---|---|
| Email Address | Read-only |
| Display Name | `mailboxes.name` |
| Personal Information _(collapsible, Pro only)_ | `first_name`, `last_name`, `title`, `phone`, `mobile`, `department`. Used by signature placeholder substitution (`{{user.first_name}}`, `{{user.title}}`, etc.) and by department-based signature resolution. Department field uses a typeahead datalist built from the domain's existing departments via `inc/get_dept_options.cfm`. Community inputs are HTML-disabled and the action handler skips the UPDATE on Community so values survive a Pro→Community downgrade. |
| Mailbox Quota (GB) | Per-mailbox override of the domain default |
| Status | `Active` / `Inactive` |
| SVF Policy | Populated from `spam_policies` where `custom='1' OR default_policy='1'` |
| Quarantine Notifications | `user_settings.report_enabled` |
| Train Bayes Filter | `user_settings.train_bayes` — with prominent warning that improperly-trained Bayes affects ALL recipients |
| Download Messages from User Portal | `user_settings.download_msg` — with malware-risk warning |
| Nextcloud Webmail | `mailboxes.nextcloud_enabled`. **Enabling for an existing user requires a new password** (NC needs the password to provision the Mail app profile) — error 51 if the admin enables NC without setting a password. **Disabling** shows a `Keep Nextcloud account data` checkbox that gates whether the NC user account and data are preserved or permanently deleted. |
| Two-Factor Authentication | `recipients.enforce_mfa`. When enabled, the user's web portal access becomes restricted to Account Settings, My App Passwords, Set Up Your Devices, and Webmail & Apps until they enroll. Email/calendar/contacts keep working throughout — only the web portal is gated. The 0→1 transition triggers an LDAP group move from `cn=one_factor` to `cn=two_factor` so Authelia challenges them on next sign-in. |
| Timezone | `user_settings.timezone` (Java `ZoneId` list). Used for the vacation auto-reply schedule and dashboard timestamps. |
| Authentication Type | Read-only — `local` or `remote` |
| Change Password _(local auth only)_ | Optional. Minimum 12 chars, no special chars, HIBP-checked. Blank keeps the current password. |

## Edit Encryption modal

Per-mailbox encryption flags (`pdf_enabled`, `smime_enabled`,
`digital_sign`, `pgp_enabled`) plus the cert/keyring generation
parameters (CA, validity, key size, algorithm, PGP key length).
Submit queues async cert + keyring generation into
`cert_generation_queue` if a flag flips on and no existing
cert/keyring is present — same lazy-queue pattern as
[Relay Recipients](https://docs.deeztek.com/books/administrator-guide/page/relay-recipients#edit-encryption-modal).

## Reset 2FA Devices modal

Single-purpose modal that clears Authelia TOTP and WebAuthn device
registrations via
`docker exec hermes_authelia authelia storage user totp delete`
and `... webauthn delete --all`. Two modes:

| Mode | What it does |
|---|---|
| **Default** | Deletes TOTP + WebAuthn devices. User stays under 2FA enforcement and re-registers on next sign-in. "User lost their phone" recovery. |
| **Nuclear** _(checkbox)_ | Also moves the user from `cn=two_factor` back to `cn=one_factor`. Admin override; if `enforce_mfa` is still 1 the next Edit Options save will reverse the LDAP move. |

> **Does not affect Duo Push.** Duo enrollments live on Duo's cloud
> servers. Use the Duo Admin Console.

## Send Mobile Setup Profile

Per-mailbox action that emails the user a signed iOS / iPadOS
mobileconfig profile pre-wired with IMAP + Submission + CalDAV +
CardDAV + the appropriate account name and email. The link in the
email expires in 30 minutes and works only once.

Handler is `inc/admin_resend_mobile_setup_action.cfm`. The
mobileconfig generator itself is shared with the user-portal Setup
Your Devices wizard.

## Rotate NC Internal Password

Visible only when `mailboxes.nextcloud_enabled = 1`. Generates a new
random local password for the Nextcloud user via
`docker exec hermes_nextcloud occ user:resetpassword` and the
displayed value is **never shown** — it is purely defense-in-depth.

Background: the Nextcloud internal password was historically set to
the user's real password, which silently allowed CalDAV/CardDAV to
accept the org password and defeat the app-password isolation
boundary (closed in #197 Phase 1). The internal password is now
random and unused by anything user-facing — users reach NC via OIDC,
and DAV/IMAP go through app passwords. This admin action lets the
admin re-randomize on demand without touching the user's actual
credentials.

## Delete

Cascading delete that mirrors the create pipeline in reverse, with
the same cleanup discipline as Relay Recipients (the goal is
zero-orphan rows). Per mailbox:

```
For the selected mailbox ID:
1. Read mailboxes row + user_settings (for ldap_username)
2. Remove LDAP from cn=mailboxes  (before delete_internal_recipients
                                    runs ldap_delete_user_relay)
3. (If NC enabled) Remove from cn=nextcloud LDAP group
4. delete_internal_recipients.cfm
     - docker exec hermes_authelia authelia storage user totp delete
     - docker exec hermes_authelia authelia storage user webauthn delete --all
     - LDAP user entry delete
     - cert_generation_queue cancel + recipient_certificates clear
     - recipient_keystores + Ciphermail keystore clear
     - wblist, mailaddr, password_reset_requests cancel
5. DELETE mailboxes WHERE id = <id>
6. DELETE sender_login_maps WHERE login_user = <email>
7. DELETE user_settings (if not already cleared by step 4)
8. Re-sync any shared mailbox vfile ACLs the user was a member of
   (so the deleted user vanishes from sharer lists)
9. DELETE app_passwords WHERE username = <email>
10. (If NC enabled AND admin did NOT check "Keep Nextcloud data")
    docker exec hermes_nextcloud occ user:delete <user>
11. signature_regen_map.cfm (rebuild body milter map without this user)
```

The Nextcloud user/data preservation is opt-in via the `Keep Nextcloud
account data` checkbox surfaced when toggling NC off in Edit Options
— deletion from this page asks the same question.

> **Dovecot mailbox data on disk is NOT deleted.**
> `/mnt/vmail/<domain>/<user>/` survives the delete. If you intend
> to permanently retire the mailbox, remove the directory from the
> host after the delete completes. This matches the per-domain
> behavior on [Domains](https://docs.deeztek.com/books/administrator-guide/page/domains-i8v).

## Local-auth vs RemoteAuth — the credential split

Identical model to relay recipients. See
[Email Relay > Relay Recipients § Local-auth vs RemoteAuth](https://docs.deeztek.com/books/administrator-guide/page/relay-recipients#local-auth-vs-remoteauth--the-credential-split)
and [Authentication Settings](https://docs.deeztek.com/books/administrator-guide/page/authentication-settings)
for the full four-credential architecture.

For mailboxes specifically: app passwords are always Hermes-issued
regardless of `auth_type`. RemoteAuth mailbox users' upstream
directory password is exposed only to the web gate (via the LDAP
overlay's pass-through bind) — never to Dovecot or the Nextcloud
Mail profile.

> **Known forward-looking gap (#102).** RemoteAuth mapping deletion
> validation in `view_remoteauth.cfm` and `edit_remoteauth_mapping.cfm`
> currently only checks `system_users` and `recipients`. When
> RemoteAuth-for-mailboxes activity grows, the validation must add a
> third query against `mailboxes` so an in-use mapping cannot be
> stranded. See
> [LDAP RemoteAuth § Deletion validation](https://docs.deeztek.com/books/administrator-guide/page/ldap-remoteauth#deletion-validation).

## Failure semantics

| What breaks | What happens |
|---|---|
| Quota not a positive number | `session.m = 15`, redirect, no DB write |
| Missing required form fields | `session.m = 20`, redirect, no DB write |
| Mailbox not found (Edit/Delete) | `session.m = 21`, redirect, no DB write |
| Password under 12 characters | `session.m = 22`, redirect, no DB write |
| Password found in HIBP breach | `session.m = 99`, redirect, no DB write |
| HIBP API unavailable | `session.m = 100`, warning banner, mailbox still rejected (fail-closed) |
| Enabling NC for existing user without setting a password | `session.m = 51`, redirect, no DB write |
| Mobile setup profile email failed but profile staged | `session.m = 83`, warning banner, link still works |
| Duplicate email (against recipients / mailboxes / aliases / virtual_recipients) | redirect to `add_mailbox.cfm` with appropriate alert |
| LDAP add fails after DB inserts succeed | DB row exists; subsequent IMAP/SMTP login fails until the LDAP entry is created (admin can re-save Edit Options or delete and re-add) |
| Nextcloud `occ user:add` fails | Mailbox creation succeeds; NC toggle effectively becomes a no-op until re-toggled |
| `cert_generation_queue` row stuck in `processing` | Surfaces in the Add Recipient / Add Mailbox alert banner via [Pending S/MIME or PGP generation](https://docs.deeztek.com/books/administrator-guide/page/relay-recipients#edit-encryption-modal); retry via the same Retry Failed Jobs button on the Relay page |

## Files and containers touched

| Path | Owner | Role |
|---|---|---|
| `config/hermes/var/www/html/admin/2/view_mailboxes.cfm` | `hermes_commandbox` | Main page + Edit Options / Edit Encryption / Reset 2FA / Delete modals |
| `config/hermes/var/www/html/admin/2/add_mailbox.cfm` | `hermes_commandbox` | Add page (single mailbox, full per-recipient stack) |
| `config/hermes/var/www/html/admin/2/inc/add_mailbox_action.cfm` | `hermes_commandbox` | Add handler — orchestrates DB + LDAP + NC + cert queue + welcome email |
| `config/hermes/var/www/html/admin/2/inc/edit_mailbox_action.cfm` | `hermes_commandbox` | Edit Options handler |
| `config/hermes/var/www/html/admin/2/inc/edit_mailbox_encryption_action.cfm` | `hermes_commandbox` | Edit Encryption handler + cert/keyring queue insertion |
| `config/hermes/var/www/html/admin/2/inc/edit_mailbox_access_control_action.cfm` | `hermes_commandbox` | Reset 2FA Devices handler (TOTP + WebAuthn clear + optional nuclear move) |
| `config/hermes/var/www/html/admin/2/inc/delete_mailbox_action.cfm` | `hermes_commandbox` | Delete cascade |
| `config/hermes/var/www/html/admin/2/inc/get_mailbox_json.cfm` | `hermes_commandbox` | AJAX hydrator for Edit Options |
| `config/hermes/var/www/html/admin/2/inc/get_dept_options.cfm` | `hermes_commandbox` | Per-domain department datalist (typeahead) |
| `config/hermes/var/www/html/admin/2/inc/ldap_add_user_mailbox.cfm` / `ldap_add_user_mailbox_remoteauth.cfm` | `hermes_commandbox` | Local / remote LDAP entry creation |
| `config/hermes/var/www/html/admin/2/inc/ldap_add_user_groups_mailbox.cfm` | `hermes_commandbox` | Group assignment: `cn=mailboxes`, `cn=one_factor` / `cn=two_factor`, `cn=nextcloud` |
| `config/hermes/var/www/html/admin/2/inc/ldap_delete_user_mailbox.cfm` | `hermes_commandbox` | LDAP entry removal on delete |
| `config/hermes/var/www/html/admin/2/inc/nextcloud_provision_user.cfm` | `hermes_commandbox` | NC user creation, random internal password, Mail app profile, initial app password |
| `config/hermes/var/www/html/admin/2/inc/rotate_nc_password_action.cfm` | `hermes_commandbox` | On-demand NC internal password rotation |
| `config/hermes/var/www/html/admin/2/inc/admin_resend_mobile_setup_action.cfm` | `hermes_commandbox` | Mobile setup profile generation + email |
| `config/hermes/var/www/html/admin/2/inc/send_mailbox_welcome_email.cfm` / `send_mailbox_welcome_email_remoteauth.cfm` | `hermes_commandbox` | Welcome email (local: reset link; remote: org-password instructions) |
| `config/hermes/var/www/html/admin/2/inc/signature_regen_map.cfm` | `hermes_commandbox` | Body milter `signature_by_sender` map + `sender_data.json` rebuild |
| `mailboxes`, `recipients`, `user_settings`, `maddr`, `sender_login_maps`, `app_passwords`, `recipient_certificates`, `recipient_keystores`, `cert_generation_queue`, `mailbox_aliases`, `shared_mailbox_permissions`, `wblist`, `password_reset_requests` | `hermes_db_server` | The mailbox row group |
| `cn=<user>,ou=users,dc=hermes,dc=local` | `hermes_ldap` | Per-mailbox LDAP entry (with `userPassword` Argon2id hash for local-auth or `seeAlso` for remote) |
| `cn=mailboxes`, `cn=one_factor` / `cn=two_factor`, `cn=nextcloud` in `ou=groups` | `hermes_ldap` | Group memberships set at create-time |
| `/mnt/vmail/<domain>/<user>/` | `hermes_dovecot` (mounted) | Mailbox directory tree — auto-created on first LMTP delivery / IMAP login; NOT removed on delete |
| Authelia `totp_configurations` + `webauthn_devices` | `hermes_authelia` storage backend | Cleared on delete + Reset 2FA Devices |
| `hermes_nextcloud` container | — | `occ user:add` / `user:delete` / `user:resetpassword` / `group:add` (the latter from [Domains](https://docs.deeztek.com/books/administrator-guide/page/domains-i8v)) |

Every shell-out uses `docker exec ...` per the standard Hermes pattern.

## Related

- [Domains](https://docs.deeztek.com/books/administrator-guide/page/domains-i8v) — mailbox-domain registration. A mailbox is
  meaningless without a domain row of `type='mailbox'`. Domain
  defaults (default quota, Nextcloud enabled, 2FA required) pre-fill
  the Add Mailbox form for new mailboxes; toggling the per-domain
  default does NOT cascade to existing mailboxes.
- [Settings](https://docs.deeztek.com/books/administrator-guide/page/settings) — global Dovecot config: TLS profile,
  compression, encryption at rest, quota warning thresholds. The
  warning thresholds measure against the per-mailbox quota set here.
- [Aliases](https://docs.deeztek.com/books/administrator-guide/page/aliases) — alias addresses that resolve to mailboxes
  (with optional silent-discard mode). Add aliases AFTER the target
  mailbox exists.
- [Shared Mailboxes](https://docs.deeztek.com/books/administrator-guide/page/shared-mailboxes) — shared-namespace
  mailboxes with per-user ACLs. Distinct from regular mailboxes —
  they live in the same `mailboxes` table but with
  `mailbox_type='shared'`.
- [Mailbox Rules](https://docs.deeztek.com/books/administrator-guide/page/mailbox-rules) — server-side Sieve rules per
  mailbox. Sieve is always-on at the protocol level via
  [Settings](https://docs.deeztek.com/books/administrator-guide/page/settings).
- [SAN Management](https://docs.deeztek.com/books/administrator-guide/page/san-management) — SAN prefixes that gate
  client auto-discovery for every mailbox domain.
- [Authentication Settings](https://docs.deeztek.com/books/administrator-guide/page/authentication-settings)
  — Authelia config, OIDC, the four-credential architecture (web
  vs IMAP/SMTP vs DAV vs Nextcloud) that mailbox app passwords
  slot into.
- [LDAP RemoteAuth](https://docs.deeztek.com/books/administrator-guide/page/ldap-remoteauth) — required
  prerequisite for `auth_type='remote'` mailboxes. The Add form
  surfaces only mappings with `enabled=1`.
- [Password Resets](https://docs.deeztek.com/books/administrator-guide/page/password-resets) — admin-driven
  password reset for local-auth mailboxes (the user-facing flow
  uses the link in the welcome email).
- [System Users](https://docs.deeztek.com/books/administrator-guide/page/system-users) — distinct from
  mailboxes; covers console admins / readers, which use the
  `system_users` table rather than `mailboxes`.
- [Email Relay > Relay Recipients](https://docs.deeztek.com/books/administrator-guide/page/relay-recipients)
  — the relay-topology equivalent. Mailbox users are delivered
  locally; relay recipients are forwarded downstream. Don't confuse
  the two.
- [Organizational Signatures](https://docs.deeztek.com/books/administrator-guide/page/organizational-signatures)
  _(Pro)_ — consumer of the Personal Information fields on the
  Edit Options modal (plus the domain's Organization Information
  fields).