Mail-flow navigation and control technology
Mail Server and Anti-Spam keep their existing pages and addresses, grouped by the part of mail handling they configure. Connection and SMTP checks precede message analysis; Rspamd checks are grouped as parallel analysis, followed by action policy and operational views. Menu order does not change native enforcement or imply a strict order among scanner checks.
Each page keeps its description at the top, followed by the control explanation and mail-flow details. Affected pages show Applied by, naming their native component, and Stage, locating the control in mail handling. Sender Relay identifies Postfix outbound routing; scanner rate limits identify Rspamd, while Quota Groups distinguish Dovecot mailbox capacity from authenticated sending allowances. Cross-stage pages such as RBL Management and Global Whitelist explain their multiple owners. These descriptions are not enabled/applied indicators: use the existing live status and validation controls for that evidence.
SMTP AUTH submission is a separate entry path and bypasses Postscreen. MX gateways forward accepted messages; mail-serving roles may deliver through Dovecot. Existing role visibility and access controls remain unchanged.
Overview
MailtronX is an open-source admin appliance for Postfix, rspamd, ClamAV and Dovecot: one dashboard for the machine, the queue
and the scanner; outcome analytics over time; blocklists you run yourself; hardened out of the box. The mail stack is Postfix (SMTP, queue,
delivery), rspamd (spam scoring, greylisting, Bayes, SPF/DKIM/DMARC, RBL checks; Postfix hands every
message to it as a milter on 127.0.0.1:11332), ClamAV (virus scanning, called by rspamd)
and Dovecot (mailbox delivery over LMTP, IMAP). There is no amavis and no SpamAssassin: rspamd does the
scoring itself and talks to ClamAV directly.
Page conventions
Every page opens with a one-paragraph description of what it does; list pages also say whether the mail engine enforces the list yet. In every table, the column that holds buttons is headed Action. A destructive action (Delete, Remove) always opens an in-app confirm dialog before it submits; Cancel or Escape backs out with nothing changed.
Navigation
The left navigation shows a colored emoji icon beside every submenu entry, matching the group headings. Group headings and submenu rows share the same dark background. The current page is marked by highlighted text, a left border and a compact glow with a bright edge; hover and keyboard focus provide visible feedback. Group headings expand or collapse their submenu without leaving the current page.
The upper-right user control shows a user icon, the username and a dropdown arrow, without a role badge. Hover or focus opens the menu; move directly into it to select Change password or Log out. The navigation starts Configure Server, Domains, Mail Server and Anti-Spam, with appliance account management under Configure Server → Manage Users.
Across MailtronX, blue action buttons use the ADC Certificates Reset appearance: a navy blue gradient, brighter hover gradient and pulsing blue glow. Green primary and success actions use the Network Settings Save and Apply gradient and green glow. Red danger actions use the ADC ACME Deactivate appearance: red text and a dark red hover background with a pulsing red glow.
The Certificates description is provider-neutral; matching appliance certificates are applied automatically. On Network Settings, Save & Apply aligns with the interface box above, with equal spacing above and below the button row.
GeoIP Country Scoring shows country flags in a compact list. Selected countries add +4 to the spam score; unselected countries receive no country score adjustment. Search by name or code, then select countries and Save. The blue controls header has three sections: selected count on the left, search and country count with Select all and Deselect all in the center, and a gray Cancel followed by a green Save button on the right. Select all and Deselect all use the shared blue style. Gray secondary buttons use the same gradient and pulsing hover treatment. All Cancel buttons and cancel links across MailtronX, including confirmation dialogs, use this shared gray style. The selected count appears only in the controls header. Secondary actions use the shared blue button gradient and pulsing hover glow, while Save uses the shared green primary-button gradient and pulsing hover glow, matching Network Settings Save and Apply; a separate blue box centers the save reminder below the country list. The scoring explanation is centered and the country list always stays open. The bottom scoring status and match metrics pane is omitted. The grid uses at most five columns and fewer on narrow screens. Country CD is displayed as Democratic Republic of the Congo. Select all and Deselect all act on the filtered countries. Saved selections are shown when you return. Authenticated senders are skipped.
Home (Dashboard)
The dashboard description is centered inside its own blue-bordered panel in the same column as System Information, with matching left and right edges. The dashboard uses one centered content column without a reserved empty spacer. On tablet-sized screens, System Information rows reflow into labels and values with their metric bars and hints underneath; the description stays above the panel and within the available content area. All top page descriptions use the same centered blue-bordered component, matching the outer boundaries of that page’s content panels or tables. Alignment updates when the viewport or content size changes. Mail-flow and backend-technology explanations share those same boundaries below the original page description. Pages without an existing compact layout use a defined desktop content width (72rem including padding); dense table, log and management pages may use the wider 88rem layout. Existing compact page layouts retain their sizes, and the shared widths shrink to the available space.
The Home page is the live status view. Everything on it refreshes from one request per second to
GET /api/metrics (see API); the request merges the agent's sysmetrics and
mailmetrics answers. System Information is centred on the dashboard; live O/S metric tiles are displayed in the shared banner. Mail metrics have their own page: Mail Metrics under Stats.
The shared banner displays live CPU, memory, fullest disk usage, load, swap and receive/transmit rates with small trend graphs on authenticated pages. The banner and dashboard share one metrics poller per page; hidden pages pause polling. Login has no live metrics polling. The login logo header uses a solid black background. The logo matches the login fields in width and preserves its original aspect ratio.
Banner Live Metrics
Compact tiles: label, live value, and a sparkline of the last 90 one-second samples (percent tiles on a fixed 0–100 scale, the others auto-scaled). Tiles: CPU %, Mem %, Disk % (the fullest discovered disk), Load (1-minute average), Swap %, Rx and Tx (network bits per second, formatted bps / Kbps / Mbps / Gbps).
System Information
Mode Type (the recorded deployment profile, also shown on System Info), hostname, management IP, OS, kernel, appliance version, uptime, load (1/5/15 min), CPU cores with a live bar, memory (used / total, available), one row per discovered disk (used / total, bar, device), swap, the AV signatures row and the system time (a client-side clock, re-synced from the server every second, time zone shown on the right).
AV signatures shows the ClamAV daily signature version and a badge: up to date
when clamd is active, freshclam is active and the daily signatures are at most 24 hours old; stale
when they are older than 24 hours or freshclam is not active; attention
when they are older than 72 hours, clamd is not active, or no signature file can be read. The badge's hover text shows the
build time of the signatures. The agent reads the header of /var/lib/clamav/daily.cld (or daily.cvd).
Mail Metrics
/mailmetrics, under Stats. The live panel refreshes from one request per second to GET /api/metrics
(the same request the Home page makes).
MAIL Live Metrics
| Tile | Meaning | Source |
|---|---|---|
| Queue | Messages waiting in Postfix right now. Hover shows the split: active, deferred, hold, incoming, maildrop. | One file per message under /var/spool/postfix/<queue>, counted by the agent. |
| SMTP conns | Established inbound connections on ports 25, 465 and 587. | /proc/net/tcp and tcp6, state ESTABLISHED. |
| Scans/min | rspamd messages scanned per minute over the last 15 minutes. | rspamd controller /stat counter scanned, sampled into a 15-minute ring. |
| Spam % | Share of scans treated as spam in that window. With no scans in the window it falls back to the all-time share (hover says which). | spam_count / scanned deltas. |
| Scan ms | Average rspamd scan time over its most recent scans (hover shows how many). | scan_times from the controller. |
The agent computes these at most every 5 seconds and serves the cached answer to the one-second poll in between, so the dashboard never costs more than one queue walk and one controller call per 5 seconds.
Mail Flow, Dispositions, Outcomes
Three count tables with 1h, 24h, 7d, 30d and 1y columns: Mail Flow sits above Dispositions in one column, Outcomes beside them; the block sits under the live panel.
- Mail Flow — In (messages queued by Postfix), Out (successful deliveries), Bounced. From the hourly
table
mt_msg_stats. - Dispositions — Delivered, Clean, Greylisted, Spam, Blocked, Deferred, Bounced, SMTP rejected.
- Outcomes — Virus, Phishing, Banned attach, Bayes spam, SPF fail, DKIM fail, DMARC fail, RBL listed, Whitelisted, Blacklisted, DNSBL block, Greet fail, HELO reject, Unknown rcpt, Relay denied, Sender reject.
Dispositions and Outcomes come from mt_outcome_stats (category, hour, count), filled by the outcome collector
(see Categories). Verdict categories (Clean, Spam, Greylisted, Blocked) are exclusive per
scanned message; delivery results are per recipient attempt; a message therefore appears in more than one group. Never sum
across groups. Whitelisted counts messages the Global Whitelist matched; Blacklisted counts blocks by the appliance's own
blocklist zone and refusals by the banned sender, domain and character-set lists (see Lists).
Statistics
Live counters
The top of the Statistics page shows rspamd's own cumulative counters (messages scanned, treated as spam / ham, rejected, in quarantine, viruses blocked, Bayes learned) and the action breakdown, read live through the agent.
Outcomes over time
A stacked bar chart rendered on the server as SVG (no JavaScript, CSP-safe). Range: last 24 hours by hour, last 7 or 30 days by day, last year by week. Series: Scan verdict (clean, spam, greylisted, blocked), Delivery result (delivered, deferred, bounced), SMTP-door rejects (the six reject reasons), Detections (virus, phishing, banned attachment, Bayes spam, SPF/DKIM/DMARC fail, RBL listed, blacklisted). Each category keeps one colour across all groups. Hover a bar segment for its exact count. A totals table for the chosen range lists every category.
Retention
Hourly rows live 90 days. A daily roll-up (mt_outcome_daily) is recomputed every night for every complete day
and hourly rows older than 89 days are purged. Windows longer than the hourly retention read the daily table for older days
and the hourly table from that boundary on, with no overlap. Budget at 26 categories: 624 hourly rows and 26 daily rows per day.
Metrics pipeline
Agent actions used by the dashboard
| Action | Returns | Notes |
|---|---|---|
sysmetrics | cpu_pct, mem_*, swap_*, load, rx_bps, tx_bps, time, and disks (list) | CPU is a 400 ms sample. |
mailmetrics | queue_* counts, smtp_conns, scans_per_min, spam_pct (+basis), scan_ms, av_* | Cached 5 s. rspamd counters sampled into a 15-minute ring; counter resets (rspamd restart) start the window over. |
service-status / restart-service | unit state / restart result | Allowlisted units only. |
Collectors (root cron)
| Script | Schedule | Writes | Reads |
|---|---|---|---|
deploy/harvest-rbl-stats.sh | every 10 min | mt_rbl_stats (zone, hour, hits, blocks) | mail.log by byte offset |
deploy/harvest-msg-stats.sh | 5-59/10 | mt_msg_stats (hour, msgs_in, msgs_out, bounced, scanned, blocked, spam, greylisted, clean) | mail.log by byte offset; rspamc stat deltas |
deploy/harvest-outcome-stats.py | 2-59/10 | mt_outcome_stats (category, hour, count) | mail.log and rspamd.log by byte offset, rename-rotation safe, each line bucketed by its own timestamp; offsets advance only after a successful insert |
deploy/rollup-outcome-stats.sh | 00:20 | mt_outcome_daily; purges hourly rows older than 89 days | mt_outcome_stats |
deploy/sync-rbl-mirrors.sh | hourly at :17 | local RBL zone mirrors for rbldnsd | upstream lists |
Dry run of the outcome collector against the rotated logs: harvest-outcome-stats.py --dry-run .1.
Categories and how they are counted
| Category | Counted when |
|---|---|
| delivered / bounced / deferred | mail.log status=sent / bounced / deferred (one per recipient attempt) |
| smtp_rejected | any NOQUEUE: reject: line (smtpd or postscreen) |
| postscreen_dnsbl (DNSBL block) | reject line containing blocked using |
| postscreen_greet (Greet fail) | postscreen PREGREET line |
| helo_reject | Helo command rejected |
| relay_denied | Relay access denied |
| unknown_recipient | Recipient address rejected |
| sender_reject | Sender address rejected |
| blacklisted | DNSBL block attributed to the appliance's own zone rbl.mailtronx (fed from the Banned IP and Global Blacklist lists) |
| clean / spam_tagged / blocked / greylisted | rspamd action no action / add header / reject / greylist on the scan line |
| virus, phishing, banned_attachment, bayes_spam | symbols CLAM_VIRUS, PHISHING, MAILTRONX_BADEXT, BAYES_SPAM |
| geo_country | symbol MAILTRONX_GEO_BLOCK (connecting IP country selected for +4 scoring) |
| spf_fail, dkim_fail, dmarc_fail | R_SPF_FAIL; R_DKIM_REJECT or R_DKIM_PERMFAIL; DMARC_POLICY_REJECT, _QUARANTINE or _SOFTFAIL |
| rbl_listed | any RBL_* symbol with a score above 0 (lookup failures score 0 and never count) |
The Postfix reply texts come from the Postfix source (smtpd_check.c, postscreen_early.c).
soft_rejected and subject_rewritten are counted in the database but not shown in the boxes.
Configure Server
Release packaging retains each version's exclusive staging directory with the release artifacts. Failed attempts remain available for inspection; packaging does not remove previous stages.
Fresh installation keeps administrator and service credentials in restricted native files. Passwords, certificate import payloads and template credentials are sent through stdin to subprocesses rather than command arguments; credentials are not printed in the install log.
Node type: fixed deployment and banner
Login Abuse Protection
Configure Server > Login Abuse Protection controls temporary client bans for repeated failed Dovecot and chat authentication, and repeated malformed IMAP/SMTP client commands. Fail2ban counts failures per source address across accounts and owns the active bans and their expiration. The initial full-mail deployment policy is five failures in ten minutes, a fifteen-minute ban and doubling repeat bans capped at one day. Existing appliances require explicit enablement; installer reruns preserve the saved policy. Pure MX gateways have no Dovecot jail.
Bans cover client ports 143, 993, 465, 587, 4190, 2000 and chat port 5222 only. They do not block SSH, administration over HTTPS or inbound server-to-server SMTP. The page shows native active-ban evidence, start and expiration time, with an audited Admin-only Unblock action. Unknown evidence is labeled UNKNOWN. Exemptions accept individual IPv4 or IPv6 addresses, not networks. Password logging stays disabled. Policy changes affect future bans; current bans keep their expiration. Failed configuration validation reports the retained recovery artifact without silently rolling back files.
Protocol protection counts explicit invalid-command disconnects and HTTP commands sent to SMTP submission ports. Ordinary disconnects, single TLS failures, SMTP greetings, successful logins and backend errors do not count. Enabled chat uses Prosody log_auth failure-only logging with the real peer IP; its jail blocks port 5222. All three jails share the saved timing and individual-IP exemptions, and appear in active-ban status. Unblock removes only that IP from the owned client jails; SSH bans are untouched.
The Dovecot 2.4 authentication-worker filter counts each failure once and ignores successful logins and unauthenticated disconnects. A misconfigured client can still generate genuine failures; use expiring bans and investigate repeats. This feature does not publish to public RBLs or lock user accounts. IP-based protection does not catch every distributed account attack.
MailtronX has two deployment types: MX Gateway (Store and Forward) and Full Mail & Groupware Server. The full server includes SMTP receiving, filtering, queuing and forwarding. Existing combined records retain full-server compatibility; Combined is not a choice for new deployments.
The blue strip below the banner displays MailtronX - [node type] on console pages. An absent deployment record shows Legacy (Type not recorded); unavailable or corrupt records show UNKNOWN. It never guesses a mode from installed packages.
The installed mode is displayed in the banner strip. There is no separate Node Type page or navigation entry, role dropdown, draft save or in-place conversion. The retired /node-role URL returns 404. Legacy type is not inferred from old drafts or current services. Old draft data is retained without changing the installation.
Select MTX_NODE_TYPE=mail before a fresh install. The installer records the type once in a root-owned deployment file and local settings. Reruns reject a different type and reject relabelling a legacy untyped installation. To change type, deploy a replacement, migrate and verify affected data, and perform controlled cutover. Retire and clean up the source only after delivery and client access are verified.
The installer separates common SMTP/security packages from mailbox packages. Select MTX_NODE_TYPE=mx for a fresh gateway and supply MTX_MX_BACKEND, an HTTPS directory authority and a pre-approved one-use invitation. SMTP stays masked until the initial directory is persisted and ready. Reruns preserve enrollment. The installer performs its explicit configuration pass; ordinary web restarts and application patches never regenerate saved configuration. Full Mail installs SOGo for CalDAV/CardDAV calendars, contacts, tasks and meeting scheduling, and the packages for Prosody text chat. Configure Server → Groupware reports service state and applies saved configuration. Chat activation requires a certificate covering every selected chat domain; its address is the mailbox email address. Installation and active service state require protocol and client acceptance testing before use.
Full Mail configuration opens client ports TCP4190 and TCP5222 through the existing managed firewall rule. Upgrades preserve SSH rules, active bans, custom rules and additional permitted ports; they do not reload the whole ruleset. Unknown firewall layouts fail validation before provider changes. If runtime and persistent configuration diverge during a write failure, inspect both native states before retrying.
0.25.7 configuration compatibility: the DAV helper discovers the active HTTPS administrator-proxy nginx source through its enabled symlink, preserving file ownership and permissions. The administrator proxy and the unique port443 listener must share the same server block. When no canonical mail-port rule exists, Full Mail/legacy configuration can adopt one unambiguous commented TCP accept rule containing the existing mail ports. Source-network restrictions, additional ports, comments and other rules are preserved; MX and ambiguous layouts are rejected.
Groupware and firewall helpers run from the release payload matching the running agent's compiled version. Source-only installations without a versioned deployment directory use the existing legacy path; an incomplete versioned payload fails instead of selecting another copy. Activating this configuration does not overwrite an operator-modified source checkout. Source tests and service startup do not replace deployment and client acceptance checks.
Installed-client groupware: calendars and contacts discover through https://<mail-host>/SOGo/dav/; task lists use CalDAV VTODO. Meeting invitations, updates, cancellations and responses use calendar scheduling and email; verify shared-calendar permissions and free/busy with the selected clients. ManageSieve on TCP4190 requires STARTTLS and stores personal rules/vacation in the mailbox home, after the mandatory quarantine before-script. XMPP text chat uses TCP5222 with TLS and the existing Dovecot mailbox identity; federation and audio/video are not enabled. New mailbox passwords and disabled account/domain flags are read from the existing SQL identity store.
Thunderbird and other CalDAV/CardDAV clients need version-specific testing. Classic Outlook requires a compatible connector for DAV; new Outlook has a different extension model. IMAP Notes folders work only in clients supporting that representation; native Outlook Notes interoperability is not established. S/MIME/OpenPGP keys remain with the client. Read receipts depend on recipient cooperation and cannot be guaranteed. Shared mailboxes and send-as require explicit tested delegation, not password sharing. A Full Mail label does not imply Exchange compatibility or high availability.
The intended pure MX profile performs full SMTP security, anti-spam, configured antivirus, queuing and forwarding. It has no Dovecot, hosted mailboxes, local mailbox quarantine, IMAP/POP, authenticated client submission, browser webmail or end-user logins. Restricted administrator and cluster communication remain. Filtering results must reach backend quarantine policy through a verified trusted path. Existing legacy nodes are not converted to this profile by this release.
The internal agent retains read-only inventory for deployment and migration tooling to inspect account records, retained/orphaned mail homes, stored messages, existing quarantine and queued mail. Collection reads bounded metadata only and changes no services or messages. Unsafe, missing or unavailable data reports UNKNOWN, not zero. This is a point-in-time check, not a migration snapshot.
The privileged agent refuses hosted mailbox operations, local routing changes and submission enabling on MX. Domains, valid users, nonempty enabled groups, forwarding addresses and alias domains come from the authoritative backend. Gateways use local Postfix socketmap lookups, reject unknown recipients without contacting the backend, and retain a durable versioned directory across restarts. Synchronization checks every 30 seconds over authenticated HTTPS. A temporary authority outage retains the last directory for at most its 24-hour lease; expired/missing state temporarily defers mail. Explicitly rejected credentials disable acceptance and persist that state. Successful updates are acknowledged; old or conflicting revisions are refused. Account passwords and mailbox contents are not exported. Accepted mail is routed directly to the configured backend, including recipients with address extensions and alias domains; gateway delivery does not fall back to the public MX records.
Initial enrollment uses native root commands: mailtronx-web gateway-controller backend.example.com on Full Mail; gateway-invite mx.example.com creates a node-bound invitation valid for 15 minutes. Capture its stdout only through a protected console or in-memory handoff. On MX, gateway-enroll https://backend.example.com mx.example.com reads the invitation from stdin. gateway-status reports revisions, directory counts/errors or peer acknowledgements; gateway-revoke mx.example.com revokes a peer. gateway-reinvite mx.example.com renews an expired unused invitation or an explicitly revoked peer; active credentials are preserved. These scoped credentials cannot authenticate to administrator APIs. Node type, hostname, IP and certificates stay local. This first directory stage does not replicate filtering policies, administrator identities, groupware or mailbox contents and does not provide backend/controller HA.
For an MX-fronted backend, restrict inbound TCP25 to the individually verified gateway addresses. Preserve client submission465/587, IMAP and HTTPS. Provider firewall allowlists are currently operator-managed and must be verified when a gateway address changes; enrollment does not automatically modify cloud firewalls.
Shared configuration synchronization
Native configuration administration reads the effective policies through the local agent, using MTX_AGENT_SOCK or /run/mailtronx/agent.sock. Preview remains read-only; an unavailable agent prevents adoption rather than substituting default settings.
Backup destination discovery accepts an absent file, an empty array, or the original writer's JSON null as an empty list. Reading a legacy empty file does not rewrite it. Malformed or partial destination entries still prevent baseline adoption.
Baseline discovery includes configured public backup destinations through their destination validator. Legacy blank RBL schedules are observed as query, matching the existing form behavior, without rewriting the source rows. Unknown schedule values still prevent adoption.
Configuration synchronization is separate from recipient-directory enrollment. An explicitly reviewed baseline on the existing cluster authority initializes a configuration epoch. Existing peers require a separate read-only or read-write configuration capability. A new or unconfigured peer cannot publish its defaults over the cluster baseline. Root administration uses gateway-config-preview epoch node.hostname, then gateway-config-adopt epoch node.hostname reviewed_digest on the authority. Grant an existing peer with gateway-config-peer enrolled.node.hostname read-only|read-write; compare its observed settings with gateway-config-compare epoch node.hostname, review differences, then enroll using gateway-config-enroll epoch node.hostname reviewed_digest. A changed baseline invalidates the review digest.
Enrolled UI saves queue durable operations using the revision displayed with that setting. Independent settings changed on different nodes synchronize independently. Related fields within a policy remain atomic. Conflicting edits to the same setting are refused for review; the newest whole configuration file never replaces unrelated settings. Origin and operation identities provide idempotent retries. Each authenticated exchange uploads pending edits and downloads cluster changes together. Received revisions and successful native activation are tracked separately: a native failure remains pending for retry while other independent settings can apply. Reconnecting nodes retain their outbox and catch up from their saved revision or a consistent snapshot; invalid epochs, schemas, revoked peers and malformed native readback do not acknowledge successful activation.
Shared policies cover HELO, antivirus, rate limits, greylisting, score thresholds, message size, login-abuse protection, time/NTP, logging, BCC archive/rules, backup retention/schedules/public destinations, patch schedules/preferences, application-update preferences, public LDAP settings/group mappings, quota groups, routing/sender relay/default relay, filter lists, selected RBL zones and country scoring selections. The original settings pages remain the editors. A queued save is not an applied-service acknowledgement. Recipient-directory acknowledgement alone does not verify shared-policy activation or mail delivery.
Domains, mailbox identities, mail-group expansion and forwarding remain with the existing MAIL/backend owner; MX receives the required recipient/alias/catchall projection. Per-node network identity, interface labels, routes, relay-trusted networks, certificates/private keys, local administrator accounts, tokens, passwords, groupware service identity and enrollment remain under their existing owners. Shared destination and relay policies never distribute local credentials. Install/reboot/restore/test actions, messages, queues, sessions and operational logs are not configuration edits. Shared backup destinations use a separate machine-identity folder for each node, preserve legacy archives, and retain only that node's archive history. Missing or duplicate machine identities require diagnosis before relying on independent backup storage.
Current configuration snapshots support up to 10,000 retained setting keys, including tombstones. Revision replay digests are retained locally; high-change-volume clusters require capacity monitoring. This configuration layer does not provide controller failover or cross-site connectivity provisioning.
Cluster recipient management
Configure Server → Cluster Management lets an Admin initialize an MX or MAIL node as the existing directory authority and register external recipient sources. A follower cannot also become an authority. For a fresh MX-only management installation, set MTX_GATEWAY_MANAGEMENT=yes with MTX_NODE_TYPE=mx and the external SMTP backend. No MAIL node or enrollment invitation is required for that first management MX. SMTP remains gated until a valid recipient directory is available; register a source, upload its complete directory, then rerun the installer to verify and enable the mail path. Other MX nodes enroll with the existing invitation workflow.
External directory adapters submit POST /api/cluster/recipients to the management node over authenticated HTTPS using a dedicated domain-scoped feed credential. Backend destinations are approved by the administrator; a recipient-feed credential cannot redirect mail. The address-only payload contains source, a positive monotonic revision, complete: true, a source valid_until no more than 24 hours ahead, domains with SMTP backend routes, and recipients. Include valid user, alias and mail-enabled group addresses; mailbox passwords and group expansion remain on the customer's backend. The supplied deploy/push-recipients.py submits a customer-generated JSON export and reads the feed credential from standard input. Vendor-specific directory connectors are not included.
Malformed, partial, unowned or conflicting uploads leave the previous working revision intact. Complete snapshots replace that source's address list; an empty replacement requires explicit allow_empty: true. Source domain scopes cannot overlap. Publication never extends an external source lease. A valid source update can be saved with HTTP 202 while another feed is missing or expired; it is not yet a published combined revision. This lets multiple expired feeds refresh independently. Pending or expired active feeds prevent publication of a new combined directory; the last valid gateway copy lasts only until its recorded expiry. The management MX serves its own local recipient lookups; enrolled gateways fetch every 30 seconds and acknowledge persisted directory revisions.
The page reports recipient-source and gateway directory revisions. Directory acknowledgement is not proof that all filtering settings, credentials, processing history or mailbox/queue data are synchronized. Shared filtering policy uses the separately enrolled configuration journal described above. Recipient publication itself does not alter filtering policy.
| Page | Path |
|---|---|
| Network Settings | /network-settings — see Network |
| Routes | /netroutes — see Routes |
| Date & Time / NTP | /timentp — see Date & Time / NTP |
| Logging Options | /logging — see Logging Options |
| Backups | /backups — see Backups |
| Patches | /patches — see Patches |
| Certificates | /certificates — see Certificates |
| Services | /services — see Services |
| SSH Access | /syscfg?what=ssh |
Quota Groups
Under Mail Server → Quota Groups, /quotagroups. Named policies for mailbox size and sending limits, scoped to one domain each so
a policy created for one domain can never be picked, edited or applied to a mailbox on another domain — the same isolation
Mail Groups already have. Every mail-enabled domain gets its own Default group automatically, starting at 4 GB and 25/hour,
100/day, 250/week; Default always exists for its domain and cannot be deleted, and a group that still has mailbox members, or
is still attached to a mail group, cannot be deleted either. Each group carries a mailbox quota in GB and a sending limit shown as
three separate columns — per hour, per day, per week — all three applying at the same time; 0 in any of them means
unlimited for that time frame. Every policy (other than Default) is tied to exactly one mail group, chosen on the create and edit
forms; the policy is named after that mail group, and every member of it inherits the policy (see below). The mail-group
picker only ever lists groups belonging to the domain chosen alongside it -- one domain'''s mail groups are never offered while
a different domain is selected, since a business may run entirely different groups per domain. A mail group already attached
to a different quota policy is left off the list entirely too, so one policy can never silently take over a mail group another
policy already governs. Default is never tied to a mail group and its name never changes. Every add, edit and delete is
audited.
How a mailbox gets its policy
A mailbox's effective policy is resolved in this order:
- an explicit pick on the mailbox's own Users form — this includes explicitly choosing Default, which is a real, stored choice distinct from never having picked anything;
- otherwise, any Mail Group in the same domain the mailbox is a member of that has a quota policy attached (set on the mail group's own page): every member of that mail group inherits it. A mailbox in more than one such mail group gets the more generous policy — the larger mailbox quota wins, and if those are equal, the larger daily sending limit wins;
- otherwise, the domain's Default group.
The Users page shows each mailbox's resolved policy with where it came from (manual, <mail group name> (mail group), or default); the edit form shows the same next to the picker.
Enforcement in Dovecot
The Enforcement line at the top of the page shows whether Dovecot is applying the mailbox quota, read live from
the appliance (the quota files on disk and the plugins the running Dovecot has loaded). Apply to Dovecot turns
enforcement on and is audited. Applying writes two files through the agent: /etc/dovecot/conf.d/99-mailtronx-quota.conf
(the quota plugin for LMTP and IMAP, imap_quota for IMAP, and one quota root using the count driver) and the userdb lookup in
/etc/dovecot/dovecot-sql.conf, which then returns each mailbox's limit (quota_storage_size) resolved from its
quota group in the order above. A dated copy of both files is kept under /var/backups/mailtronx/dovecot/, the
configuration is tested with doveconf -n before Dovecot is reloaded, and a failed test or reload is reverted.
Because the limit is looked up at every login and every delivery, changing a policy, a mailbox's pick or a mail-group attachment takes effect immediately: nothing has to be re-applied. Apply is needed once, and again only if the Dovecot files are replaced by hand (the Enforcement line then reads not enforced). With enforcement on, a delivery to a full mailbox is refused at LMTP with Quota exceeded (mailbox for user is full) and the sender receives the bounce; IMAP clients that support the QUOTA extension show the mailbox's usage; and the Users page shows each mailbox's current usage against its limit in the Used column (a dash until enforcement is applied). A quota of 0 GB means unlimited. Usage is what Dovecot counts in the mailbox indexes (count driver), not raw disk blocks.
Sending limits
The per hour / day / week values are enforced on Submission (SMTP AUTH), the only path on which a user is authenticated. At the end of every message the mail server asks the appliance's policy service (loopback only) whether the authenticated user may send: the user's effective policy is resolved in the order above, and the user's accepted messages in the last hour, the last 24 hours and the last 7 days are counted — rolling windows, exact counts, and a message with several recipients counts once. When any of the three limits is reached the message is refused with a permanent error the mail client shows, Sending limit reached: N messages per hour/day/week (policy P). Try again later, and nothing is queued. Below the limits the send is recorded and the message continues through the other checks. A limit of 0 means unlimited for that window. If the policy service is unreachable the mail server answers with a temporary error (451) and the client retries later — it never sends unchecked. The Users page shows each mailbox's counts in its Sent column (hour / day / week). The rspamd rate-limit buckets (Rate limits) stay in place as a separate burst guard; the sending limits are the policy an administrator sets per group.
Services
/services. Status of mariadb, nginx, postfix, dovecot, rspamd, redis-server (rspamd's store), clamav-daemon, clamav-freshclam,
rbldnsd, unbound, fail2ban, chrony and mailtronx-agent, each with a restart button. Clicking restart opens a confirm dialog. On confirm the icon spins, the
button is disabled and the badge reads restarting until the page returns with the real status; the server restarts
the unit through the agent (systemctl restart, allowlisted units only) and records an audit entry. mailtronx-web
is not listed on purpose: restarting it from its own page would drop the page.
Date & Time / NTP
The paired panels retain matching heights with a compact six-line NTP server entry area; longer lists remain scrollable.
/timentp. Time & NTP: the IANA time zone (applied with timedatectl), the
time-security mode and the list of time servers, one per line. Secure (NTS) authenticates each server over TLS
(NTS-KE on TCP 4460 plus NTP on UDP 123) and needs hostnames; Insecure (NTP) accepts hostnames, pools or IPs.
Switching the mode swaps in that mode's default servers when the list still holds the other mode's defaults; the picker adds
known NTS servers. Apply writes the servers to the appliance's own chrony sources file and reloads chrony (a dated copy of the
previous file is kept under /var/backups/mailtronx/chrony/); the built-in chrony defaults keep applying alongside. A new appliance starts in America/Los_Angeles (installer variable MTX_TIMEZONE) with the five NTS servers time.cloudflare.com, nts.netnod.se, ptbtime1.ptb.de, ptbtime2.ptb.de and ptbtime3.ptb.de already configured. The installer also creates a swapfile when the system has no swap (the size of the memory, at least 2 GB and at most 4 GB), because the virus scanner and the rspamd workers need headroom on small appliances; the Home page shows swap use.
Current Time & NTP State (refreshed every 5 seconds): local and UTC time, zone, synchronisation state,
active source, stratum, offset (green under 50 ms, amber under 100 ms, red above), root delay and dispersion, leap status,
last sync, and each configured server with the live source it resolved to, its state (synced, candidate, backup) and offset,
or a DNS-failure marker when the name does not resolve. API: GET /api/time, POST /api/time/set
(form fields timezone, ntp_servers newline-separated, ntp_nts, set_ntp, csrf).
Changes and rejected requests are audited.
Logging Options
/logging. Remote syslog target: every log line of the appliance (mail server, scanner, delivery,
system and the appliance's own audit-relevant messages) is forwarded by rsyslog to one receiver, given as host and port, over
TCP, TCP + TLS or UDP. TCP and TLS use a disk-assisted queue (up to 100 MB under
/var/spool/rsyslog) so a receiver outage never loses lines: they are spooled and retried until the receiver is back.
UDP is best effort. With TLS the receiver certificate is verified: against the system CA bundle, or against a private
CA pasted on the page (kept in /etc/mailtronx/syslog-ca.pem until removed); a host-name target is also
checked against the certificate name (the exact name, or a wildcard certificate of its parent domain), an IP target only
against the chain. Test connection performs the TCP
handshake, reports the latency and, for TLS, the verified certificate (subject, issuer, expiry, TLS version) or the failure
reason together with the issuer the receiver presented, so the right CA can be pasted. Leaving the host empty stops forwarding.
Save writes the managed file /etc/rsyslog.d/99-mailtronx-forward.conf, validates the whole rsyslog configuration
(rsyslogd -N1) and restarts rsyslog; a failure restores every file and restarts again. On the first Save a
hand-written forwarding block at the end of /etc/rsyslog.conf, if one exists, is retired into the managed file (dated
copies of every changed file are kept under /var/backups/mailtronx/rsyslog/). Local retention sets the
on-disk rotation of the syslog, mail and auth logs: 1-3650 rotates daily and keeps that many days; 0 keeps the packaged policy
(weekly, 4 rotations); the new policy is dry-run tested with logrotate before it is kept. Audit retention removes
audit-log entries older than the given days once a day (0 = keep forever). Every save is audited.
Forwarder status (refreshed every 10 seconds): the target and protocol, whether the receiver answers a TCP
handshake and the round-trip latency, whether rsyslog holds an established connection to it, the spool backlog (bytes and queue
files waiting), the log volume as journal lines per second, minute and hour, the rsyslog service state, and the last forwarder
message from the rsyslog journal in the past hour. The header shows ENCRYPTED for TLS and FORWARDER CONNECTED while the
connection is up. API: GET /api/logging, POST /api/logging/test, POST /api/logging/set
(form fields target, port, protocol, ca_pem, ca_clear, local_days,
audit_days, csrf).
Backups
/backups. Two kinds, both stored under /var/lib/mailtronx/backups/ as encrypted archives (a gzip tar sealed
with openssl AES-256-CBC, PBKDF2 600,000 rounds, salted). Backup encryption initializes automatically with a cryptographically random 256-bit key unique to each fresh appliance. Existing customer passphrases and keys are preserved across upgrades and same-appliance restores. Cluster members use independent keys; keys are never supplied in release packages. Customer deployment images must not include an initialized backup key. Settings controls archive retention and optionally accepts a custom passphrase of at least 12 characters. Download recovery key is an Admin-only POST protected by CSRF and returns an uncached attachment. Save this copy securely outside the appliance: after appliance loss, backups cannot be decrypted without it. A restore requires the encryption key or original passphrase. Before changing a key, retain its recovery copy for older archives.
Nothing under /var/vmail is ever archived, and the network identity (interface addresses, hostname) is left out of both
kinds so an archive restored onto another machine never takes over the address of the original.
Configuration Snapshots
A snapshot holds the configuration tables of the appliance database (administrators, domains, mailboxes, mail groups, group
members, lists, quota groups, RBL zones, settings; never the audit log, the statistics or the send log) and the files the
appliance manages under /etc: Postfix, Dovecot, rspamd (local.d with the maps), nftables, rbldnsd, unbound, dnsmasq,
the log-forwarding file, the rotation policy, the chrony sources and the agent's site drop-in; no private keys, no
credentials. One is taken automatically before every configuration-changing action (reason BEFORE_APPLY, at most one
per minute) and on demand with Take Snapshot (an optional label). Restore (passphrase required, confirmed) works in
place: a PRE_RESTORE snapshot of the current state is taken first, the archived files replace the current ones, the tables are
imported and the schema updates re-applied, then Postfix, Dovecot, rspamd, nftables and rsyslog are validated; if any check
fails, files and tables go back to the pre-restore snapshot and the error is shown. On success the mail services reload.
Full-Appliance Backups
A full backup adds every database table, /etc/ssl/mailtronx (certificates, private keys, the ACME account and DNS
credentials), /etc/mailtronx (database credentials, session key, the backup passphrase and settings), the two service
units, the web-server sites, the main rsyslog file, the scanner's state under /var/lib/rspamd and a fresh Redis dump
(Bayes, fuzzy, rate limits). Backup Now takes one; Download saves the encrypted file elsewhere; Restore
replaces everything on this appliance (Redis is stopped, its dump replaced and started again; mailtronx-web restarts at once and
the agent a few seconds later); Restore from File uploads an archive (up to 64 MB) and restores it in one step, which is
how a fresh appliance of the same or a newer version is populated after the software is installed (the agent raises the web
server's request-body limit to 64 MB for this on its first start). Uploaded archives are kept in the full-backup list until deleted. Every archive has a readable sidecar (.json) describing what it holds.
Opening an archive by hand: openssl enc -d -aes-256-cbc -md sha256 -pbkdf2 -iter 600000 -in <file> | tar -tz
(members: manifest.json, db.sql, redis/dump.rdb in a full backup, and the archived paths relative
to /). Settings, snapshots, backups, restores, uploads, downloads and deletions are all audited.
Destinations
Off-box copies of full backups (snapshots stay on the appliance). Every full backup, manual or scheduled, is copied to
each enabled destination right after it is written, and each destination keeps its newest N archives (Keep). Types:
NFS share (server, export, mount options; the agent mounts it under /var/lib/mailtronx/mounts/ only while
copying and unmounts it again, so an unreachable server never wedges the appliance), S3-compatible storage (endpoint,
region, bucket, access key and secret; provider Other unless the service needs its own name), SFTP (host, port,
user, password or a PEM private key; the host key is pinned on first use), FTPS (explicit AUTH TLS on 21 or implicit TLS
on 990), Dropbox and Google Drive (a token produced by rclone authorize dropbox or
rclone authorize drive on any desktop with a browser, pasted into the form; Google Drive uses the drive.file scope
and accepts an optional client ID and secret of your own, which Google recommends). All remote types go through rclone with a
configuration the agent renders from the stored settings (secrets root-only, passwords obscured); the archives themselves stay
encrypted with the backup passphrase. Test writes, reads back and removes a probe file. Push newest copies the
newest full backup now. Browse lists the archives on a destination; Fetch to appliance pulls one into the
full-backup list, from where it is restored like any other archive. Removing a destination leaves the copies there untouched.
Schedule
A systemd timer (mailtronx-backup.timer, written by the agent) runs a full-appliance backup daily or weekly at the
given local time (up to five minutes of random delay), pushes it to every enabled destination and applies the retention counts;
a run missed because the appliance was off is made up at the next boot. The result of every run is in the journal of
mailtronx-backup.service.
Patches
Node patch status stays compact on the left; Patch scheduler uses the remaining row width with controls in organized rows. Their top and bottom edges align; on smaller screens they stack vertically.
/patches. Node patch status: how many operating-system updates are available (with the package list),
when the package lists were last refreshed, whether the system asks for a reboot (and which packages asked), the running kernel,
the outcome of the last run and the next scheduled run. Check for updates refreshes the package lists (Debian's
main, security and updates suites as configured on the appliance); Dry Run refreshes and shows what a run would do without
changing anything; Run Now (confirmed) applies every available update. A run: takes a configuration snapshot first
(Backups, when a passphrase is set), refreshes the lists, upgrades every package without prompts while keeping the appliance's
own edited configuration files, allows new dependencies (for example a new kernel) but never removes packages, records the
event, and when the system then requires a reboot and reboot automatically is on, reboots the appliance one minute later
(the event says rebooting; with the switch off it says deferred and the status keeps showing reboot required).
A run happens in the background; the page refreshes every five seconds until it finishes. Only one run at a time.
Patch scheduler: a systemd timer (mailtronx-patch.timer, written by the agent) starts a run weekly on the
chosen day or daily, at the given local time (up to five minutes of random delay; a missed run is made up at the next boot).
Patch History keeps the last 100 events: time (UTC), kind (SCHEDULED, MANUAL, DRY-RUN, CHECK), exit code, package
count, the packages, the reboot outcome, the duration, and the tail of the apt output. Every check, dry run, run, setting and
schedule change is audited.
Application updates
Release builds retain the signed package, feed and signature. Their duplicate unpacked build stage is removed only after signature, package hash and complete file/ownership/mode checks pass. A failed verification retains the stage for investigation.
The Application panel updates MailtronX itself from a signed release feed. The feed is three files
published together (on mailtronx.com or as the assets of a GitHub release): feed.json (version, package file name, size,
SHA-256, publication time, notes), feed.json.sig (an ed25519 signature of the exact manifest bytes) and the package
(mailtronx-<version>-linux-amd64.tar.gz: both binaries, the schema updates, the runtime scripts, VERSION and the source
commit). The appliance verifies the signature with the MailtronX release public key built into it and ignores any feed that does not
verify. Check for updates fetches and verifies the manifest and compares its version with the running one; only the request
reaches the feed host, and the daily automatic check is off unless switched on. Install downloads the package, verifies
its size and SHA-256 against the signed manifest, unpacks it into /var/lib/mailtronx/releases/<version>/ next to the
earlier releases, applies the shipped schema updates, installs the binaries and restarts both appliance services a few seconds later
(the page reloads by itself). Kept releases lists every version present on the appliance (built here or installed from the
feed) with the running and installed one; Rollback reinstalls any kept version the same way. Maintainers produce a release
with scripts/package-release.sh (a tagged, clean checkout and the private signing key, which never ships).
Certificates
/certificates. The appliance keeps a certificate store; every certificate has a name, and the
table lists common name, SANs, issuer, expiry with days left, a status dot (red under 30 days, amber under 60),
type (single, multi-SAN, wildcard) and which services use it. The assignment is recorded on the appliance and restored automatically after an installer run, so an upgrade never replaces the certificate a service uses. Installer reruns reuse an unexpired matching certificate even when renewal is approaching; renewal remains with the configured certificate issuer or operator. A new appliance obtains its certificate during installation: Let's Encrypt over HTTP-01 when the appliance's name resolves to it in public DNS (installer variable MTX_ACME_EMAIL for the account contact), or a certificate handed to the installer (MTX_TLS_CERT / MTX_TLS_KEY) for private appliances; when neither is possible the installer stops and prints the DNS record to create. There is no self-signed certificate. Filter tabs and the search box narrow the
table. Private keys never leave the appliance: they are stored root-only and are never shown or exported.
| Action | What it does |
|---|---|
| Import | Paste the leaf certificate, optional intermediates and the private key (PEM). The key must match the certificate and the certificate must not be expired. |
| Self-Signed | Generate a certificate from a common name, SANs (hostnames or IPs), key algorithm (ECDSA P-256 / P-384, RSA 2048 / 3072 / 4096) and validity (1–3650 days). |
| Export | Download certificate plus chain as PEM. |
| Automatic application | Import, issuance and renewal apply a matching appliance certificate to nginx, Postfix and Dovecot. Each configuration is validated, services are reloaded, and the actual HTTPS, SMTP STARTTLS and IMAPS certificates are verified. Encrypted recovery restores previous files and configurations on failure. Used By shows actual assignments. |
| Delete | Removes a certificate that no service uses (second click within five seconds confirms). |
Let's Encrypt (ACME)
The Let's Encrypt tab issues and renews certificates from any ACME directory. Register an ACME account once (directory URL and contact email; the default directory is Let's Encrypt production; any other ACME CA's directory URL can be entered). Issue takes a name, common name, optional SANs and the challenge type:
- HTTP-01: the CA fetches
http://<hostname>/.well-known/acme-challenge/<token>; the appliance serves that path on port 80 and redirects everything else to HTTPS. Port 80 must be reachable from the Internet for every hostname in the certificate. Wildcards are not possible. - DNS-01: the appliance publishes the
_acme-challenge.<name>TXT record through the DNS provider's API, waits until the zone's authoritative servers answer with it, lets the CA validate, then removes it. Under DNS-01 Configuration pick the provider that hosts the names (the catalog of the lego project, over two hundred providers such as DigitalOcean, ClouDNS, Cloudflare, Route 53) and enter the credentials it lists; RFC 2136 (your own name server), the last entry, is for a name server of your own that accepts dynamic updates signed with a TSIG key (server, key name, secret, zone). Test connection publishes and removes a test record through the provider (under the domain given in Test with domain). Credentials are stored root-only on the appliance and never logged. Required for wildcards.
An account that already exists elsewhere, for example the one a CAA record names, is imported instead of registered: on this page under Import an existing account (the key as PEM or certbot's private_key.json; the CA is asked for the account that owns the key and nothing is created; the previous account files are kept as .bak), or at install time through MTX_ACME_ACCOUNT_KEY and MTX_ACME_ACCOUNT_URL, so that the very first certificate is issued under it. The installer creates a new account only when no key is given. The tag next to ACME Account says which kind is in use: IMPORTED ACCOUNT or APPLIANCE-CREATED, and the Origin row says whether the appliance's account came from the installer or this page. Forget account removes the account from this appliance only (key and record kept as .bak); nothing is ever sent to the CA, so an account shared with other clients stays valid for them. The appliance has no way to deactivate an account at the CA.
Issued certificates land in the store and matching appliance certificates are automatically applied to the required services. Auto-renewal has one setting,
Renew after N days (7 to 60, default 60): every ACME certificate is replaced by a new one N days after it was issued,
whatever its remaining life (set 7 for a weekly certificate), and the services that use it are reloaded. The floor is 7 days
because Let's Encrypt issues at most 5 certificates per exact set of names every 7 days. The rule is checked hourly; a
certificate whose renewal fails is retried once a day; Run Renewal Now renews everything that is due at once. The
setting lives in /etc/ssl/mailtronx/acme/renewal.json. The account key and the TSIG secret are stored root-only
on the appliance. API: /api/acme/account (+/set, /import, /forget),
/api/acme/dns/providers, /api/acme/dns01 (+/set, /test, /delete), /api/acme/renewals (+/set, /run), POST /api/acme/issue,
/api/acme/renewals (+/run).
The certificate the appliance shipped with appears as appliance. API: GET /api/certs;
POST /api/certs/import, /api/certs/assign (form field
services, comma-separated), /api/certs/delete (all with csrf);
GET /api/certs/export?name=. Every action and every rejected request is audited.
Network Settings
/network-settings is the Network Settings page. The legacy /netinfo page and its navigation links are removed; /netinfo2 redirects to /network-settings for existing bookmarks. It includes: hostname, domain, Internal DNS Servers,
internal search domains (comma-separated and optional), interface configuration under Configure Network Interfaces and live status under Live Network Interfaces Settings, with one added Public DNS over TLS selector. The header states that internal domains and private lookups use Internal DNS Servers in either mode; without public TLS those servers also handle public queries. With public TLS, the page explains split DNS: internal domains and private lookups use internal DNS, while ordinary public queries use the selected provider over verified TLS; internal failures never fall back to public DNS. The DNS routing explanation appears in its own blue-bordered panel aligned with the network panels; it has no status badge. The Network Settings title and status remain in the settings panel below. The Network Settings header uses the NTP-style green lock badge, PUBLIC DNS ENCRYPTED, only when saved managed TLS configuration is active without conflicts. No selected TLS provider displays PUBLIC DNS: USES INTERNAL DNS, matching the selector. Use internal can be saved when switching off an existing TLS provider; all queries then use Internal DNS Servers, while internal routing rules remain in place. The dedicated public TLS service is stopped only after dnsmasq switches to internal upstreams. Saved settings awaiting activation display NOT APPLIED; a configured managed resolver that is not running displays TLS INACTIVE. Conflicting or unavailable status is labelled separately. Status refreshes every 30 seconds without changing unsaved fields. All three section headers have explicit left, center and right div areas. Network Settings, Configure Network Interfaces and Live Network Interfaces Settings titles are left-aligned; the settings status remains centered, with encryption status and live refresh information on the right. Its Network Settings label column is 200px wide to accommodate Internal DNS Servers. Both network tables hide conventional Docker internal interfaces (docker0, br- followed by 12 hexadecimal characters, and veth followed by hexadecimal characters). The default-route interface stays visible, including in container deployments. Other bridges, VLANs and bonds remain visible. This is a display filter; hidden interfaces remain in the saved configuration and are not removed. Scheduler controls use the helper from the active signed release; fresh installations use the installed deployment helper until a retained release is activated. MailtronX collectors, patch schedules and backup schedules are managed in root’s crontab; existing times and enabled states are preserved. Debian maintenance jobs remain in their package-owned locations. Retired MailtronX cron.d entries are quarantined and MailtronX timers are disabled. Cron runs on the appliance’s local clock; unlike the former timers, missed runs are not automatically replayed and scheduled starts do not include the former five-minute randomized timer delay. Only the main interface offers an editable Gateway field. Other static interfaces display centered white Disabled text on a dark gray (#2c3036) background; this display text is never submitted as a gateway address. Configure Network Interfaces uses fixed field widths: Interface 90px, Node 220px, IP and Gateway 140px, Subnet 70px and MTU 64px; Label uses the remaining space. Narrow views scroll within the table instead of squeezing fields. Hostname changes require a dotted FQDN up to 63 characters, with valid nonempty DNS parts; an unchanged legacy short hostname is preserved so unrelated edits remain possible. Domain and search domains share the same strict DNS-label validation in TLS and internal-only modes, with at most six search domains and six internal IPv4 servers. Loopback, unspecified, multicast, broadcast and IPv6 resolver inputs are rejected. Interface IPs must be usable unicast IPv4 host addresses, with unique addresses and subnet prefixes from /8 to /32; network and broadcast addresses are rejected for /8 through /30, while /31 and /32 remain supported. Both pages load the shared browser validator before their page script; the agent repeats the checks before Apply. Interface labels are optional free text, at most 40 characters without control characters, and must refer to configured interfaces. Invalid labels reject the whole request. Labels are persisted together only after the agent accepts the network settings; rejected network requests cannot change labels. Configured interface aliases such as eth0:1 inherit their parent link MTU, displayed read-only; MTU changes belong on the parent interface. Both pages accept digits only, at most four characters, and require MTU 576–9000 before submission; the agent also enforces this range. Address values accept both numeric CIDR and dotted-mask slash notation without rewriting the original stanza. All dnsmasq settings, including split DNS and RBL routing, use /etc/dnsmasq.conf. App-managed split-DNS directives are delimited by BEGIN/END MailtronX managed split DNS comments. Operator comments outside the block remain byte-for-byte intact; comments inside it are retained on Apply. Comment-only edits do not trigger configuration-drift warnings; active directive changes do. Migration validates that every existing active directive survives, verifies an encrypted recovery archive, validates native dnsmasq syntax and quarantines the old include outside the active configuration directory. Unbound retains its separate TLS configuration. Internal DNS changes while TLS is managed use Network DNS (/netinfo2), so the legacy writer cannot bypass TLS routing. When managed DNS over TLS is configured, Internal DNS Servers displays the internal domain, reverse and single-label forwarding targets, deduplicated in configured order. The local public TLS resolver at 127.0.0.1 is excluded from that field. The DNS field help text omits the resolver configuration file path. Label-only saves preserve the network configuration. Only the live main interface may configure the default gateway. Edited gateways must be unicast, within the interface subnet, different from the interface, network and broadcast addresses, and resolve to a reachable link neighbor before configuration changes. Existing unchanged cloud on-link routes are preserved. New-subnet gateway migrations that cannot be verified on the current link require console-assisted configuration. Recovery captures all IPv4 main-table default routes independently of which interface changed; rollback restores their devices, gateway, metric and on-link attributes before removing unexpected defaults. The page shows validation progress before applying network changes, a server-derived 30-second confirmation countdown after Apply, and recovery status when the Apply response is lost. Keep is disabled when the countdown expires. Polling continues through temporary connection loss; the server-side rollback deadline does not depend on the browser remaining connected. Confirmation expires at the recorded deadline. Failed rollback retains recovery files, disables Keep and reports the error for a Revert retry or console recovery. Configured interface aliases such as eth0:1 are accepted for label and network saves; unconfigured interfaces and malformed names remain rejected. Interface label edits preserve an existing dotted hostname; fully qualified hostnames are not extended with the Domain value a second time.
TLS is recommended for public DNS queries. Select Quad9 Over TLS, Cloudflare Over TLS or Google Public DNS Over TLS for automatically configured pairs of verified TLS endpoints. Custom is not offered. DNS Apply requires an encrypted configuration backup. If backup encryption is not configured, set a passphrase under Configure Server > Backups > Settings before retrying; DNS remains unchanged when backup creation fails. Use internal DNS servers retains ordinary system DNS: the servers listed in Internal DNS Servers handle internal and public queries until a TLS provider is selected and Save & Apply is used.
There are no Add/Remove controls or a separate routing-preview panel. Cancel restores the selected provider and normalized saved endpoint settings.
The Internal DNS Servers field supplies private/internal DNS servers when TLS is selected. Domain and Search domains route there automatically;
single-label names and private reverse lookups remain internal with no public fallback. Save & Apply validates TLS settings before any network change.
If network changes require confirmation, TLS activation waits for Keep; Revert and confirmation expiry discard the pending TLS activation.
The dedicated public resolver uses loopback port 5336, separate from Rspamd resolution on 5335. A comment immediately above the local nameserver identifies dnsmasq and its upstream routing configuration. Resolver fingerprints ignore comments and blank lines while detecting changes to active settings. MailtronX places a managed ownership header in resolv.conf and retains that header, resolver options and operator comments during Network and DNS saves. Resolver ownership checks must also account for NetworkManager, systemd-resolved, DHCP hooks and cloud-init; the header is documentation, not a filesystem lock. Fresh deployments also set immutable protection with chattr +i. MailtronX temporarily unlocks protected resolv.conf for an authorized save or rollback and relocks it on success or failure. The persisted protection comment enables startup recovery after interruption. Existing mutable deployments retain their current policy. The unified dnsmasq file retains operator comments and separates public TLS, internal domains, private reverse lookups and single-label routing into documented blocks. No plaintext or recursive fallback is used for ordinary public DNS.
Settings are appliance-local in cfg_dns2; activation checks staged native configurations and provider connectivity,
takes encrypted recovery, preserves existing blocklist routes and rejects configuration conflicts.
Network
/network-settings has a DNS routing explanation followed by the settings and live interfaces panels.
Network Settings
Hostname (letters, digits, hyphens), Domain, DNS resolvers,
Search domains, and one row per interface configured in /etc/network/interfaces
(ifupdown): Label (free text, stored by mailtronx-web), Node (the hostname), IP, Subnet prefix, Gateway, MTU.
Interfaces that are not static only expose MTU. Editing enables Cancel and
Save & Apply.
DNS resolvers follow the box's resolver design: when a local dnsmasq is the system resolver
(resolv.conf points at 127.0.0.1 and dnsmasq is active) the field manages dnsmasq's upstream server=
entries and dnsmasq is restarted on apply; otherwise it manages the nameserver lines of
/etc/resolv.conf. The help text under the field says which applies. Search domains and the domain
always go to resolv.conf; the domain also updates the 127.0.1.1 line of /etc/hosts.
/var/lib/mailtronx/netpending, writes the new configuration, applies it live (addresses, default
route and MTU with ip, no link flap; hostnamectl; dnsmasq restart) and starts a 30-second timer. The page
shows the pending banner with a countdown: Keep commits the change and discards the backups;
Revert now or the timer expiring restores the backups and the previous live settings. If the agent
restarts while a change is pending, it reverts on startup. A second change cannot be applied while one is pending.Network Interfaces
Live table, refreshed every 5 seconds: link state dot, interface with its label, addresses (IPv4 and IPv6, link-local excluded), MAC, MTU, and throughput as received / transmitted bits per second from the kernel counters.
API: GET /api/net/status, GET /api/net/config, GET /api/net/state,
POST /api/net/apply (form fields config JSON, labels JSON, csrf),
POST /api/net/confirm, POST /api/net/revert. Every apply, confirm and revert is audited.
Routes
/netroutes. Add Static Routes: destination network (IPv4 CIDR), gateway (IPv4) and an optional
metric. The gateway must lie in the subnet of one of the configured static interfaces; that interface becomes the route's
device. The route is applied immediately and written into that interface's configuration as a tagged
post-up / pre-down pair, so it returns after a reboot without any extra script. The default
route is not managed here: it is the gateway on Network Settings. Before a configuration file is changed, a copy is kept
under /var/backups/mailtronx/network/.
Routes lists the live IPv4 kernel routing table (refreshed every 5 seconds): destination, gateway, device with its label, whether the row is one of the static routes, and Edit / Delete on static rows. Edit loads the row into the form and turns Add into Save (the old route is removed and the new one added); Delete asks for a second click within five seconds. The panel header shows how many static routes are configured.
API: GET /api/net/routes, POST /api/net/routes/add (form fields to, via,
metric, csrf), POST /api/net/routes/del (to, via, csrf).
Additions, deletions and rejected requests are audited.
Mail Server
Every list page opens with a short description of what the list does and whether the mail engine enforces it yet.
| Page | Path | Entry format |
|---|---|---|
| Routing | /list/routing | domain -> transport:[nexthop]:port (e.g. example.com -> smtp:[relay.example.net]:25) or user@domain -> ... for one recipient; plus the default relay host row — see Routing and relay host |
| Sender Relay | /list/sender-relay | sender@domain -> [relay.example.net]:587 or @domain -> [relay]:port; plus the relay credentials rows — see Sender relay and credentials |
| Forwardings | /list/forwardings | user@domain -> dest@domain (one mailbox), @domain -> dest@domain (every address in the domain to one destination), @domain -> @otherdomain (same user in another domain); several destinations separated by commas. Applied immediately through the mail server's virtual alias map, which reads the list on every delivery. A whole-domain rule accepts mail for any address in that domain. |
| BCC by Sender | /list/bcc-sender | sender@domain -> copy@domain or @domain -> copy@domain; one copy address per rule — see BCC copies and archive |
| BCC by Recipient | /list/bcc-recipient | recipient@domain -> copy@domain or @domain -> copy@domain; plus the Archive address row — see BCC copies and archive |
| Submission (SMTP AUTH) | /submission | status page with Enable / Disable — see Submission |
| Allow Relaying | /list/allow-relaying | address, network (192.0.2.0/24) or host name — see Routing and relay host |
| Rate Limits / Throttling | /syscfg?what=ratelimit | read-only view of the live rspamd rate-limit configuration (see Rate limits) |
| Attachment Size Restrictions | /attachment-size | MB — Postfix message size limit through the agent. |
| Mail Log | /maillog | live view with filter (?live=1&q=) |
| MAILLOG2 | /maillog2 | mail and spam message viewer with a separate diagnostic event view, search highlighting, live/pause and related log details |
MAILLOG2: mail and spam messages
MAILLOG2 reads the current local mail and Rspamd logs through the privileged agent. The default Real messages view groups recorded queue activity into message rows and excludes connection-only probes, health checks and startup diagnostics. Actual locally submitted messages remain visible, including those submitted through 127.0.0.1. Select Mail client activity for recorded IMAP/POP3 sessions, login failures, disconnects and authenticated SMTP submission, including STARTTLS and implicit-TLS submissions. The Account column shows the username when logged. This view includes connection attempts without successful authentication; it does not infer mail read or folder activity that the server has not logged. Select All log events to inspect all connection failures and service diagnostics. Classic Mail Log remains available.
The blue control bar offers search by address, IP, queue ID or text, service and outcome filters, time windows, pagination and five-second live refresh. Pause to inspect a stable view; opening details suspends refresh until closed. Search matches receive a yellow background in rows and details. Existing filtered URLs without an explicit view retain the All log events search behavior; new filter URLs record the selected view. Counts distinguish filtered rows from total reconstructed messages and raw log events. An empty Real messages search offers Search All log events for diagnostic entries. Search also checks the available related log entries.
Red text highlights failure terms such as disconnected, rejected, failed, improper, aborted, errors, lost connection, SSL_accept error and map file is not found (with or without is). Green highlights connected, delivered, accepted, successful and Trusted IP. Unknown is bold pink; NOQUEUE and NO QUEUE are bold yellow. The entire improper command pipelining phrase is red. IP addresses are cyan; domain names are blue. Whole-word matching avoids coloring disconnected as connected; negative phrases take precedence. Log text is displayed as text, never executable markup. Color alone does not establish a delivery outcome or malicious activity.
Sender, recipient and client IP are filled from available events in the same queue lifetime when the source value is unambiguous. Details identify enriched fields and show the raw entry and related trail. Queue removal or a new client assignment separates reused queue IDs. Missing values are labeled Not recorded; older context outside the retained window cannot be reconstructed. A row may have several recipients, and its displayed outcome is the latest recorded message outcome, not proof that every recipient has the same result. Sent means successful SMTP handoff, not necessarily final mailbox delivery.
Details also show filter action and spam score when recorded. Scanned means a recorded Rspamd verdict; startup messages registering an antivirus engine are not virus detections, and references to greylisting configuration are not deferred mail. Temporary SMTP rejection codes and explicit Rspamd verdicts determine deferral.
The viewer reads the latest 2 MiB and at most 2,000 events from each fixed local source (/var/log/mail.log and /var/log/rspamd/rspamd.log), up to 4,000 events total. A busy diagnostic log therefore does not displace the other source. Real-message counts are reconstructed rows within this window, not lifetime mail totals. Rotated history and other nodes are not aggregated; time filters apply within the available window. Source failures are reported, stale loaded data remains visible with an error, and an empty log is distinct from a failed read. Password/token fields are redacted where recognized; access uses existing authenticated read permissions. All filtering and inspection are read-only.
Routing and relay host
Routing (/list/routing) is Postfix's transport table. A route domain -> transport:[nexthop]:port
sends mail for that domain through the given transport: example.com -> smtp:[relay.example.net]:25 hands it to a relay,
.example.com covers the subdomains, error:no route to this domain refuses, discard: drops. A key
user@domain routes one recipient only; Postfix searches user@domain first, then the domain, then .domain,
so the per-recipient route wins over the domain route. Postfix reads
the routes live from the appliance database through the MySQL map /etc/postfix/mysql-transport-maps.cf
(transport_maps), like the Forwardings and mailbox maps: a change on the page is effective at once, with no reload. The
Relay host row on the same page sets relayhost for all other outbound mail: empty means direct delivery to the
recipients' mail servers; a host and port send everything there. With a user name the agent writes the SMTP AUTH password file
(/etc/postfix/mailtronx-sasl-passwd, root only) and enables authentication for the outbound connection; the password is never
shown again and a blank password field keeps the stored one. Host, port and user name are kept in the app settings.
Allow Relaying (/list/allow-relaying) is Postfix's mynetworks: the file
/etc/postfix/mailtronx-relay-networks holds the entries and is referenced next to 127.0.0.0/8. Listed addresses
and networks may relay outbound mail through the appliance and skip postscreen and the sender checks (permit_mynetworks).
A host name is resolved once when the list is applied and stored as its addresses; an entry that does not resolve blocks the apply with
a message. Both pages show an Enforcement row with the live state read from Postfix.
Sender relay and credentials
Sender Relay (/list/sender-relay) is Postfix's sender_dependent_relayhost_maps: a rule
sender@domain -> [relay.example.net]:587 sends outbound mail from that sender through that relay host,
@domain -> [relay]:port does it for every sender of the domain, and the per-address rule wins because Postfix looks up
sender@domain before @domain. Precedence between the pages, as Postfix defines it: a Routing route for the
recipient wins over a sender rule, and a sender rule wins over the default relay host. One relay per sender. The rules are read live
from the appliance database through the MySQL map /etc/postfix/mysql-sender-relayhost-maps.cf, so a change is effective at
once with no reload. The Credentials rows on the same page are the SMTP AUTH user and password Postfix presents to a
relay: keyed by the relay host ([host]:port) or by the sender (sender@domain or @domain). A
sender-keyed entry switches Postfix's sender-dependent authentication on, which also stops the reuse of outbound connections between
senders. All credentials, including the default relay host's from the Routing page, live in one root-only file on the appliance
(/etc/postfix/mailtronx-sasl-passwd); passwords are never shown again, and a relay without an entry gets no
authentication attempt. Adding or removing credentials reloads Postfix once.
BCC copies and archive
BCC by Sender (/list/bcc-sender) and BCC by Recipient (/list/bcc-recipient) are
Postfix's sender_bcc_maps and recipient_bcc_maps. A rule user@domain -> copy@domain adds a hidden copy of
every message from (or to) that address; @domain -> copy@domain does it for every address of the domain, and a per-address
rule wins over the domain rule because Postfix looks up user@domain before @domain. One copy address per rule: the
mail server adds a single copy per match, so a mail group is the way to reach several people. The copy is added when the message
enters the appliance, never for mail the appliance forwards or generates itself, so a rule cannot loop; the sender is not told, and
a copy that cannot be delivered does not bounce back to the sender. Postfix reads both lists live from the appliance database through
the MySQL maps /etc/postfix/mysql-sender-bcc-maps.cf and mysql-recipient-bcc-maps.cf, so a change on either
page is effective at once with no reload. The Archive row on the BCC by Recipient page is always_bcc: one
address that receives a copy of every message that enters the appliance, in either direction, regardless of the rules; empty means
off. The address is kept in the app settings; saving it is the one change that reloads Postfix. The first change on either page
connects the maps through the agent; the pages show whether they are in place.
Olefy Python maintenance
Production release packages exclude development test scripts. Those scripts remain in development source and are distributed separately as an optional developer-test archive for deliberate testing.
MailtronX releases carry a pinned Python runtime for Office-macro scanning under /opt/olefy. Its installer and application update agent maintain Olefy's virtual environment separately from Debian's system Python. OS patching continues to maintain Debian Python; it does not update the private scanner runtime. The narrow runtime upgrade preserves scanner dependencies and service configuration, retains recovery copies, and verifies the new interpreter before restarting Olefy. Appliances upgrading from an agent that predates this integration require a one-time runtime migration from the verified release; later application updates handle it automatically. Application rollback does not automatically downgrade this security runtime.
SMTP message framing
MailtronX explicitly enables Postfix newline normalization and outbound stray CR/LF cleanup. SMTP message termination requires the standard CRLF-dot-CRLF boundary. These protections include trusted networks and the loopback replay listener; relay permission does not exempt a connection from framing checks. Strict unauthenticated command pipelining checks also apply on submission ports 587 and 465. TLS and authenticated relay requirements remain in force.
The installer and configuration regeneration apply the same safeguards on each node role. Native validation and verified encrypted recovery precede activation. Failed application reports the retained recovery reference for inspection; it does not silently revert configuration. These protocol safeguards are separate from attacker classification and shared IP bans.
Submission (SMTP AUTH)
/submission. Lets mail clients (desktop and phone mail apps) send through the appliance. Enable submission
opens two services on the mail server: port 587 (STARTTLS required before anything else) and port 465 (TLS
from the first byte). Both authenticate the client against the mailbox accounts through Dovecot SASL (mechanisms PLAIN and LOGIN, accepted
only inside TLS), using the same address and password the mailbox uses for IMAP. Port 25 is not changed: it stays the inbound door and
offers no authentication.
Rules applied on the two submission ports, and only there: a client must authenticate before it can do anything; an authenticated user may send only with their own mailbox address as the sender (a different address, or a mail-group address, is refused); relaying to any destination is allowed only for authenticated users; and at the end of every message the sending limits of the user's quota group are checked (see Sending limits). Authenticated mail still passes through the anti-spam engine like every other message.
The page shows the live state read from the appliance: whether the two services exist and their ports are listening, whether the SASL
socket is present, whether the sending-limit policy service is listening, and the TLS certificate the mail server presents (assigned under
Configure Server → Certificates). Enable writes the Dovecot SASL drop-in, a sender-login map, the mail server parameters and the two
services through the agent, testing the configuration before each reload and reverting on failure; Disable removes the two
services again. Both are audited. Dated copies of every changed file are kept under /var/backups/mailtronx/postfix/ and
/var/backups/mailtronx/dovecot/.
ClamAV engine upgrades: signature updates do not patch the scanner engine. When the configured Debian repository has not yet published a security fix, use exact patched Debian packages verified against the archive's signed package index. deploy/upgrade-clamav.sh installs only the supplied ClamAV packages and required library, preserves /etc/clamav/*.conf, and restarts the existing scanner and signature updater. It does not add another repository. Verify current host recovery first and confirm the running daemon version, AppArmor enforcement, signature updates and SMTP antivirus rejection afterward.
Local blocklist query logging: the installer and each feed registration prepare /var/lib/rbldns/query.log for the rbldnsd service account without truncating existing observations. The dataset directory remains root-owned to match systemd's state-directory ownership. The existing statistics collector reads the query log on its configured schedule.
Local nameserver metadata: the appliance supplies the address 127.0.0.2 for its local nameserver rbl.mailtronx, providing the glue needed by its RBL zones. This is DNS metadata and does not add a blocked IP. New installations receive it from the installer; existing customer appliances apply it during the next scheduled feed registration after installing the application release. The resolver remains local to the appliance.
MX maintenance: pure MX gateways omit unused submission restriction parameters; existing inactive values can be preserved as comments with deploy/quarantine-unused-submission.py after verifying that no service references them. The active port 25 restrictions remain in place. SSH defaults disable protocol compression to avoid compressed-packet and shared-context issues. Fail2ban uses UTC in its own service environment to interpret UTC journal entries correctly; this does not change the appliance timezone or ban thresholds.
Rspamd Console
Anti-Spam → Rspamd Console opens within the MailtronX content area, keeping the banner and navigation available. Every console asset and controller request requires the existing MailtronX sign-in or API token. Admin and Ops accounts can change anti-spam settings; read-only accounts can view the console but cannot change settings, reset counters or train messages. Browser changes must originate from this same MailtronX site. Rspamd retains its native tabs; local appliance integrations continue to use the loopback controller. The embedded directory URL preserves Rspamd's relative asset and API requests. Theme assets use the MailtronX build hash so updates refresh cached styles. Tables, charts, forms (including search and score inputs), page selectors and panels use the MailtronX dark navy palette while chart series and status colors retain their meaning.
Manage Users
The old /users address redirects to /manage-users; all account, directory and token forms use the new address. The retired DNS Configuration page (/syscfg?what=dns) and its navigation link are removed; DNS settings are managed through Network Settings.
Configure Server → Manage Users (/manage-users) is one page for everything about who may sign in: the accounts, the LDAP / Active
Directory connection, the group-to-role mappings and the API tokens. Changes need the Admin role.
Accounts and roles
Every account has one of three roles; the role is checked on every request, so a change takes effect at once, and a deleted account is signed out immediately.
| Role | May change | May view |
|---|---|---|
| Admin | everything | everything |
| Ops | Mail Server, Anti-Spam, Domains, Stats (quarantine, greylist) and Diagnostics | everything |
| Read-only | only its own password | everything |
Changes under Configure Server (network, DNS, time, logging, backups, patches, certificates, quota groups,
services, SSH, Manage Users) need the Admin role; that includes downloading a backup and exporting a
certificate. A refused change shows a "Not permitted" page (or a JSON error with status 403 for the API) and is written to
the audit log as perm-denied. A read-only account sees a badge in the status bar and its change buttons are
disabled; the server refuses the change regardless of the browser.
The table shows Username, Display name, E-mail, Source (LOCAL or LDAP), Role and Last seen.
Add User creates a local account (username of letters, digits, dot, dash, underscore; password of at least 8
characters; role, default Read-only). Modify changes a local account's role, display name and e-mail and optionally
resets its password. Delete removes an account. Nobody can change or delete the account they are signed in with
(use Change Password for your own password), and the last Admin account is marked PROTECTED: it cannot be demoted or deleted.
Every operation and every refusal is audited. The header shows the username without a role badge. The command line creates or resets
a local Admin account: mailtronx-web create-admin <user> with the password in MTX_NEW_ADMIN_PW.
LDAP / Active Directory
The panel holds the connection: State (enabled for sign-in), Server URL (ldaps:// on 636 recommended; ldap://
on 389 is unencrypted), Base DN, Bind DN (a service account, as user@domain or a full DN), Bind password, User filter
(%s is replaced with the user name; the default finds an Active Directory user by sAMAccountName), Group
attribute (memberOf) and Skip TLS verify for internal or self-signed certificates (the connection stays encrypted; only
the chain is not validated). Test Bind connects and binds with the service account and lists the groups the service
account itself belongs to, which is a quick way to copy group DNs into the mappings. The settings, including the bind password, are
kept root-only on the appliance by the agent; the web console never holds the password and a blank password field keeps the stored one.
AD Group → Role Mappings map a group DN to Admin, Ops or Read-only. A directory user must belong to at least one mapped group; the highest-privilege matching role wins; no match means the sign-in is denied.
Sign-in: a user name that has a local account is checked locally and never falls through to the directory. Otherwise,
with LDAP enabled, the agent binds with the service account, searches the user through the filter, binds again as the user with the
typed password, and returns display name, e-mail and groups. The groups are mapped to a role, and the account is cached in the accounts
table with source LDAP, refreshed on every sign-in. Directory accounts have no local password (Change Password refuses
them) and their role is not editable on the page. Failed directory sign-ins count against the same throttle as local ones and are
audited with the reason.
API Tokens
The API Tokens panel creates bearer tokens for scripts and other systems. A token is mtx_ followed by 40 random
characters; it is shown once when created and stored only as a SHA-256 hash. Each token carries a role with exactly the meaning the
account roles have (Admin, Ops, Read-only), an optional expiry (30, 90 or 365 days, or never) and a name that appears in the audit log
as token:<name>. Rotate issues a new secret for the same name and role and invalidates the old one
at once; Revoke refuses the token from then on but keeps the row for the audit trail until you Delete
it. Last use is recorded (at most once a minute). Invalid tokens count against the same per-address throttle as failed logins.
A request with Authorization: Bearer <token> is authenticated as that token on every route: no session
cookie and no CSRF field are needed, and the same permission gate applies (an Ops token cannot change Configure Server; a Read-only token cannot change anything). Expired or revoked tokens receive status 401 with a JSON error.
GET /api/whoami returns {"principal","role","via"} for the caller.
Anti-Spam
Mail Flow & Security
/security-flow, the first page under Anti-Spam, is a compact diagram of message processing inside MailtronX: connection screening, Postfix SMTP rules, message data, Rspamd analysis, policy action, queue acceptance, then inbox, quarantine or SMTP forwarding. It does not diagram server roles or topology. Rspamd authentication, reputation, content, ClamAV antivirus and Olefy macro checks are grouped together; DNS and Redis support are shown within the scanning stage. Optional checks depend on configuration. Connection and SMTP rules can reject or defer before scanning; with the current SMTP security policy applied, new IPs passing Postscreen are handed to smtpd without a reconnect-only deferral. Pregreet, DNSBL and smtpd framing safeguards remain enforced. Configured Allow Relaying peers skip only redundant Rspamd greylisting; spam scoring and antivirus checks remain active. Authenticated submission skips Postscreen. The chart is an explanation, not a live trace or enabled-module inventory. The panel and diagram are centered in the content area, matching the application layout. The chart uses normal application text sizing and a 900-pixel maximum drawing width, with clear vertical spacing between stages and text lines. The four Rspamd groups use a centered two-by-two table with matching column widths measured from the content. Labels do not word-wrap; the analysis box grows to fit its contents. The connector above Rspamd has room around both bends and the arrowhead. The outer analysis box sizes itself around those groups and their footer, with DNS and Redis on separate lines and clear gaps from the lower cells and outer border; smaller screens scroll the diagram horizontally. The diagram contains no navigation links, so clicking its boxes does not leave the page. Footer shortcuts open message tracing, RBL management, thresholds and the Rspamd console. Clicking within the chart does not add a cyan frame; keyboard focus retains a subtle indicator. No filtering policy is changed by this page.
Engine & Thresholds
/engine. Default rspamd action thresholds: greylist at score 4, add header
(tag as spam) at 6, reject at 12 (/etc/rspamd/local.d/actions.conf). rspamd adds the
headers X-Spamd-Result, X-Spam-Status, X-Spam-Flag and Authentication-Results. Postfix restrictions in force: postscreen
with DNSBL sites (weighted: zen.spamhaus.org ×3, rbl.mailtronx ×3, feodo.rbl.mailtronx ×3, b.barracudacentral.org, bl.spamcop.net,
psbl.rbl.mailtronx, list.dnswl.org ×−2; threshold 3, action enforce), pregreet enforce; HELO must be a valid FQDN; sender and
recipient domains must exist; relaying only for mynetworks. Local networks bypass postscreen and the restrictions.
Country greylisting: the Greylisting page includes an enabled-by-default option to always greylist countries selected on Country / Geo Blocking, independently of spam score. The application enforces greylisting through both the policy API and legacy commands. The country-specific option remains configurable. Native retry timing and delay exemptions remain in force; a valid retry can pass after the configured delay. Country selection alone does not hard-block mail, and stronger security rejections still apply. Disabling the country option restores ordinary score-based eligibility. The GeoIP Country Scoring description states that selected countries add +4 to the spam score and mail from those countries is greylisted by default; authenticated senders are skipped. The country option is marked [Recommended] and [Default: enabled]. Its selected-of-total country count uses the same catalog and saved country codes as Country / Geo Blocking; this is configured coverage, not a count of currently delayed messages. The page focuses on country greylisting, fixed defaults and observed rejection history. Advanced sender IP grouping fields are not displayed; saving the policy preserves their current effective values.
The native smtp-security-apply action applies this policy with verified encrypted recovery and parser checks. Application patches do not automatically regenerate configured mail policy. Greylisting policy changes retain other greylist settings and the existing relay-map exemption. A custom whitelisted_ip declaration is preserved and reported for an explicit merge, rather than overwritten. No provider-wide IP bypass or new relay permission is added.
rspamd modules
Beyond the defaults (antivirus, dkim, dkim_signing, dmarc, spf, arc, rbl, phishing, fuzzy_check, greylist, ratelimit, multimap, milter_headers, neural, bayes_expiry, replies, history_redis, whitelist, maillist, once_received, mime_types, chartable, force_actions, settings, asn, hfilter, trie, mid, metadata_exporter, forged_recipients) the appliance enables the modules below (configuration under /etc/rspamd/local.d/):
| Module | Purpose | Notes |
|---|---|---|
| reputation | Sender reputation (IP/subnet/ASN/country, SPF domain, DKIM domain) with time decay in Redis; symbols SENDER_REP_HAM/SPAM, SPF_REPUTATION_HAM/SPAM, DKIM_REPUTATION | scores in local.d/groups.conf; needs traffic history before it scores |
| known_senders | Remembers senders from the tracked freemail domains: first-time sender +0.5 (UNKNOWN_SENDER), known sender -1.0 (KNOWN_SENDER); replies verified with the replies module | Redis set, 30-day TTL |
| spamtrap | Mail to a trap address is learned as spam (Bayes); patterns in maps.d/spamtrap.map (default /^spamtrap@.*$/) | Create a mailbox matching the pattern under Domains (for example spamtrap@example.com), or Postfix rejects the recipient before rspamd sees it. Mail from mynetworks senders is skipped by the module, so only external mail trains Bayes. |
| url_redirector | Dereferences short links (hosts in maps.d/redirectors.inc) before URL blacklist checks; cached in Redis 1 day | starter host list, edit freely |
| history_redis | Per-message scan history in Redis: 5000 rows, 30-day expiry, shown in the Rspamd Console | Answers "why was this message handled that way" without a per-message database. |
| mx_check | Verifies the sender domain runs a reachable mail server (MX resolve + TCP probe to port 25) | Needs outbound TCP 25 from the appliance; if a firewall blocks it, every external domain scores MX_INVALID. Probe results are cached in Redis. |
| external_services / oletools | Office-macro analysis: office attachments (by extension, MIME type or detected type) go to the olefy daemon (127.0.0.1:10050, unit olefy.service, user olefy, oletools in /opt/olefy/venv), which runs olevba; symbol OLETOOLS fires when a macro combines an AutoExec and a Suspicious function (rspamd default mode). Parts under 500 bytes are skipped; results are cached in Redis for 1 day. | olefy is the wrapper daemon the rspamd documentation refers to (github.com/HeinleinSupport/olefy); installer deploy/mailstack/olefy/install-olefy.sh; configuration local.d/external_services.conf. Not an antivirus: it only analyses macros. |
| rbl (senderscore) | Health monitoring is off for the senderscore_reputation RBL (local.d/rbl.conf) | score.senderscore.com answers every query (it is a reputation score, not a blocklist), so rspamd's fixed-address health probe cannot be used with it. |
Rate limits
rspamd's ratelimit module (/etc/rspamd/local.d/ratelimit.conf) applies token buckets stored in Redis.
Every message takes one token from each bucket it matches; a bucket refills at its rate and holds at most
burst tokens. When a bucket is empty the message is soft-rejected (SMTP 4xx: the sending
server queues it and retries later; nothing is lost) and the scan log carries the symbol R_RATELIMIT_INFO.
A message must pass every bucket it matches. Requests from the command line or the Rspamd Console are never limited,
and the recipients postmaster and mailer-daemon are exempt. Bounces are messages with an
empty envelope sender (or a bounce address such as mailer-daemon). The limits are starting values for an inbound
appliance; tune them from real traffic.
| Limit | Keyed on | Burst | Refill |
|---|---|---|---|
| to | recipient | 100 | 1 per minute |
| to_ip | recipient + sender IP | 30 | 3 per minute |
| to_ip_from | recipient + sender IP + envelope sender | 15 | 2 per minute |
| bounce_to | bounces per recipient | 2 | 2 per 5 minutes |
| bounce_to_ip | bounces per recipient + sender IP | 2 | 1 per 5 minutes |
| user | authenticated (outbound) user | 30 | 10 per minute |
Buckets expire from Redis after two days without traffic. With a Redis shared between appliances the limits are enforced fleet-wide. Postfix's own connection-rate limits (anvil) are all 0, so rate limiting is done only here.
| Page | Path | Entry format |
|---|---|---|
| Rspamd Console | /rspamd-console | Rspamd's native console, embedded. |
| HELO Restrictions | /syscfg?what=helo | |
| Antivirus Policy | /syscfg?what=av | |
| Greylisting | /greylist | Required. MailtronX manages fixed defaults: score 4, minimum retry delay 300 seconds, record lifetime 86400 seconds. These values cannot be tuned through the application. Country-based eligibility remains configurable. |
Lists
| List | Path | Entry | Note |
|---|---|---|---|
| Banned IP Addresses | /list/banned-ip | IP address or CIDR | Feeds the local RBL zone used by postscreen. |
| Banned Domain Names | /list/banned-domain | Domain | Envelope sender or From header domain: 15 points (refused). |
| Banned Word List | /list/banned-words | Word | Whole word in the text, case-insensitive: 4 points once (greylist threshold). Prone to false positives and easily evaded; prefer Bayes and scoring. |
| Banned Phrases | /list/banned-phrases | Phrase | Phrase in the text, case-insensitive: 4 points once. Same caution. |
| Banned Email Addresses | /list/banned-email | Email address | Envelope sender or From header address: 15 points (refused). |
| Character-Set Restrictions | /list/charset-restrictions | Charset (e.g. GB2312, KOI8-R) | Declared in a Content-Type of the message or any part: 15 points (refused). Rejects legitimate international mail; use sparingly. |
| File-Type Restrictions | /list/filetype-restrictions | Extension or type (.exe, .scr, .js) | Written to rspamd's MAILTRONX_BADEXT multimap (score 15). |
| Global Blacklist | /list/global-bl | Sender, IP or domain | Addresses feed the local RBL zone with Banned IP; senders and domains are refused by the scanner with the Banned lists. |
| Global Whitelist | /list/global-wl | Sender, IP or domain | Connecting address, envelope sender or sender domain: -20 points (spam scoring never refuses it; virus scanning still applies); addresses are also permitted through postscreen. |
| GeoIP Country Scoring | /list/geo-restrictions | Country flags, names and codes | Selected countries add +4; unselected countries have no country score adjustment. See Country scoring. |
How the lists are enforced. Every change on one of these pages rewrites the scanner's maps (one file per list under
local.d/maps.d), the multimap rules and the settings file through the agent, tests the configuration and reloads rspamd;
the Global Whitelist also rewrites the postscreen access file (/etc/postfix/mailtronx-whitelist.cidr) and reloads Postfix.
Each page shows an Enforcement row with the live number of entries in the scanner. The banned lists never apply to authenticated
(submission) senders. A whitelisted address, sender or sender domain is never checked against the banned lists at all (their symbols are
disabled for it through the scanner's settings), and its -20 keeps the other spam scoring from refusing it. Scores are fixed in this
version: 15 for the refusing lists, 4 for words and phrases, -20 for the whitelist; the engine thresholds are greylist 4, add header 6,
reject 12. Symbols in the log and headers: MAILTRONX_BANNED_DOMAIN / _EMAIL (with a _MIME twin for
the From header), MAILTRONX_BANNED_WORD, MAILTRONX_BANNED_PHRASE, MAILTRONX_BANNED_CHARSET,
MAILTRONX_WL_IP / _FROM / _DOMAIN. The outcome collector counts refusals by the banned lists as
Blacklisted and whitelist matches as Whitelisted.
Country scoring
/list/geo-restrictions is the single country-scoring page. Search the flag list by country name or code, select countries, then click Save. Selected countries add +4 to the spam score; unselected countries receive no country adjustment. Country scoring combines with the other spam checks and their configured thresholds. Authenticated senders are skipped. Saved selections remain checked on later visits.
rspamd resolves the connecting IP country through its asn module. The selected country map is applied through the existing agent operation, with configuration validation before reload. The page shows live scoring status and country-score matches for 24 hours, 7 days and 30 days. Both legacy country-page URLs redirect here.
RBL Management
/rbl-management. The appliance runs its own RBL resolver. rbldnsd on 127.0.0.2 serves the appliance's
own zone rbl.mailtronx (built from Banned IP Addresses and the Global Blacklist, rebuilt on change) and one mirrored
zone per free feed in the table below; unbound on 127.0.0.1:5335 is the recursive resolver for the public
query zones and for rspamd. Local zones are refreshed hourly by the feed sync (root cron) and the SMTP door never queries the
providers directly; a failed or empty download keeps the previous zone file, and the zone list rbldnsd serves is written by the
sync from the zone files present. The page shows per-zone hit and block counts from mt_rbl_stats for the chosen
window and, for local zones, the number of entries and the last sync time of the zone file.
| Zone | Source | Terms (read 2026-09-29) | Weight seeded |
|---|---|---|---|
rbl.mailtronx | own lists: Banned IP Addresses, Global Blacklist | — | as configured |
feodo.rbl.mailtronx | abuse.ch Feodo Tracker botnet C2 | CC0 | as configured |
abuseipdb.rbl.mailtronx | AbuseIPDB IPv4 feed published by mailtronx.com; mirrored into a dedicated local rbldnsd zone for weighted postscreen decisions | See the provider account terms for redistribution and use | 1, enabled by default |
psbl.rbl.mailtronx | PSBL passive spam block list (rsync mirror) | free mirror | as configured |
etcompromised.rbl.mailtronx | Emerging Threats known-compromised hosts | ET Open (BSD-style licence) | 2 |
blocklistde.rbl.mailtronx | blocklist.de fail2ban-reported attackers, last 48 h | "as they are, use at your own risk"; regenerated every 30 min | 1 |
cinsarmy.rbl.mailtronx | CINS Army poor-reputation IPs (capped at 15,000) | "use in any way you see fit" | 1 |
torexit.rbl.mailtronx | Tor Project bulk exit list | public list, no terms on the endpoint | 1 |
Rspamd checks the connecting sender IP against each enabled blocklist zone with a positive weight. A matching zone adds +4 to the message score, matching Country / Geo Blocking and reaching the greylist threshold. Disabled zones and negative-weight allow zones do not add this score. On a timed retry, Rspamd scans the message again and adds the RBL score again if the IP remains listed. Greylisting passes that retry, but the score remains active for the later action thresholds.
Not mirrored, on purpose: Spamhaus DROP goes into the firewall set mailtronx_drop instead (all traffic
from those netblocks is dropped on every port); DShield (CC BY-NC-SA, non-commercial) and GreenSnow
("reproduction or republication strictly prohibited") cannot be redistributed inside an appliance other operators may run
commercially; FireHOL Level 1 aggregates DShield, DROP and Feodo (redundant, and it carries the DShield clause);
Emerging Threats Block IPs is Spamhaus DROP plus abuse.ch C2, already covered; abuse.ch SSLBL was
deprecated by abuse.ch on 2025-01-03 and its download carries no addresses. Public zones without a free mirror
(zen.spamhaus.org, bl.spamcop.net, b.barracudacentral.org, list.dnswl.org) stay query zones through unbound.
AbuseIPDB is an email reputation zone only. The appliance downloads the published text feed from https://mailtronx.com/feeds/abuseipdb.txt, validates public IPv4 entries, and atomically updates /var/lib/rbldns/abuseipdb. rbldnsd serves it as abuseipdb.rbl.mailtronx; its weight and enabled state are managed here with the other local RBLs. It is enabled by default at weight 1 and contributes to the weighted postscreen decision. The site publisher currently caps the feed at 10,000 unique IPv4 addresses; a larger API result fails refresh validation rather than being silently truncated. This is an email reputation zone: it does not add addresses to nftables or block non-mail traffic. For a manual refresh that leaves Spamhaus DROP and the other mirrors untouched, run /opt/mailtronx/deploy/sync-rbl-mirrors.sh --abuseipdb-only as root.
Weights and threshold: edit weights for local and public zones and local sync schedules in the tables, then use Save changes once to save all those edits together. Saved weight changes are pushed to postscreen; Add, Remove, Enable and Disable remain separate actions. Apply now pushes the whole enabled set again after a release seeded new zones or after a change made by hand. postscreen adds the weight of every zone that lists the connecting client and refuses the connection at the threshold shown on the page, so a zone at weight 3 refuses on its own while the reputation feeds at 1 or 2 need corroboration; an allow zone carries a negative weight. Check an IP asks every configured zone the way the SMTP door does (local zones at rbldnsd, public zones through unbound), reports the firewall DROP set, and shows whether the address sits in Banned IP Addresses, the Global Blacklist or the Global Whitelist (exact or CIDR), with the summed-weight context. Sync-Schedule is a label for how a zone stays current; query zones are never synced.
Domains
/domains, table mt_domains. The Enable / Disable action is the one switch for
a domain: disabling it takes it out of Quota Groups' and Mail Groups' domain pickers immediately, and mailboxes on it can no
longer receive mail until it is re-enabled -- there is no separate "mail-enabled" state to manage. Delete
removes everything scoped to that domain in the same step: its mailboxes, its mail groups and their members, and its quota
groups -- nothing is left behind referencing a domain that no longer exists.
Catch-all and alias of are two settings on each domain row (Save). A catch-all address receives
mail for every address of the domain that does not exist as a mailbox, mail group or forwarding; it can be a mailbox here or an
address anywhere. An alias domain delivers mail for user@alias to user@target, the user part kept, where
the target is another enabled domain of this appliance; an alias domain needs no mailboxes of its own. Either setting, not both.
Postfix reads them live through the domain alias map (mysql-virtual-domain-maps.cf, answering the @domain
lookup by its whole key), behind the group, forwarding and mailbox identity maps: Postfix searches user@domain first and @domain
last, and the identity map answers every existing mailbox with itself, so a catch-all or an alias never captures a real mailbox, a
group or a single-address forwarding. The first save connects the maps (virtual_alias_maps) through the agent; the
Domains page shows whether they are in place. No reload is needed for later changes.
Domains and their mailboxes (mt_mailboxes;
Maildir under /var/vmail/<domain>/<local>, provisioned by the agent; each mailbox is in one
quota group, and the Users page's Used column shows its current usage against that
quota once enforcement is on), Mail Groups
(/groups: name required, members; each one belongs to exactly one domain, shown in its own Domain column;
the Domains page'''s Groups count for a domain links to this list filtered to just that domain, not every group everywhere.
Its own page lets you rename it and change its address, and shows which quota policy is attached, if
any — attaching or detaching one is done from Quota Groups, not here; changing the address to a different domain detaches
any attached policy, since policies are per domain) and Domain SMTP Validation (/domainsmtp, see below).
Multiple mail servers and shared DKIM
Before installation or system configuration changes, preserve the appliance's recoverable VM snapshot and current configuration. Fresh cloud installations adopt only the known systemd-resolved resolver symlinks, preserving their contents and an independently verified compressed recovery copy before replacement. Unknown symlinks stop installation for owner review. Existing appliances retain their DNS configuration. After an incomplete first installation, MTX_INITIALIZE_DNS=1 explicitly initializes the shipped DNS template; use it only after verifying recovery and the intended upstreams. It is not an upgrade default.
Each public receiving host needs an address record, reverse DNS and a domain MX record with its chosen preference. A host address alone does not make it an MX. Validate from the public appliances. DNS records and credentials are deployment state; they are never embedded in the source.
The default DKIM selector is mail, independent of the appliance hostname. For the same domain, all nodes using that selector must have the same signing key. Publish one mail._domainkey.<domain> TXT record. Use a separate key for each domain. An independent node key requires a distinct selector and its own DNS record.
On a new appliance, supply MTX_DKIM_KEY as a restricted native file containing the existing domain key. The installer imports it instead of generating another key. Alternatively, root may stream the key over authenticated SSH into python3 /opt/mailtronx/deploy/dkim-key.py --domain <domain>. The importer reads private material from stdin, prints only the public record, keeps matching keys, and refuses to overwrite a different existing key. It creates files readable only by root and the Rspamd group. Preserve recovery before replacing an existing node's key; upgrades do not replace configured keys.
For rotation, prepare a new selector (for example mail2027) with dkim-key.py --domain <domain> --selector mail2027, distribute the same domain key to every participating node, publish its DNS record, and verify public DNS before activating it in Rspamd. Keep the old selector and key until queued messages and DNS caches have expired. This import tool prepares keys; it does not activate selectors, synchronize nodes automatically, or implement cluster failover.
MTA-STS has one policy per recipient domain. Its HTTPS endpoint needs a trusted certificate for mta-sts.<domain>; the policy must list every permitted MX hostname, independently of MX preference. Publish and verify the policy before refreshing the _mta-sts TXT id. TLS-RPT needs one _smtp._tls record and a working report recipient per domain, not a record per MX host.
For provisioning an intentionally shared console password, use MTX_ADMIN_PASSWORD_FILE (mode 400 or 600) or the existing MTX_ADMIN_PASSWORD input. These affect only the first account creation; re-running the installer preserves existing admin credentials. At successful completion of a fresh interactive install, the initial administrator password is displayed directly in the terminal, outside the installation log. Automated installs print retrieval instructions instead. Generated initial passwords are kept in the restricted native /etc/mailtronx/initial-admin-password file. The console username is admin; save the credential in your password manager. Existing-account reruns never display or reset an old initial password. Password changes on existing nodes remain explicit administrator actions.
Domain SMTP Validation resolves selectors and key paths from Rspamd's effective configuration, including domain overrides and multiple selectors during rotation. It does not infer a selector from a hostname or key filename. Unreadable keys fail; dynamic configurations that cannot be resolved are reported as not checked. A DKIM PASS and propagation Partial are separate results: Partial means responding public resolvers disagree with the authoritative record. Lower TTLs affect new cache entries; existing entries retain their previous lifetime.
Domain SMTP Validation
Domains > Domain SMTP Validation (/domainsmtp?domain=<domain>, also the validate link on every row of the Domains page) shows the health of a domain's public e-mail presence, one line per check with the record or setting to fix. Each line carries a status (PASS, FAIL, WARN, NOT SET, NOT CHECKED) and a level: REQUIRED (a protocol rule or a requirement Gmail and Outlook.com apply to senders; missing = FAIL), RECOMMENDED (adds protection, no prerequisite; missing = NOT SET), OPTIONAL (needs something else first or does not apply here; missing = NOT SET). Facts that are not checks (which name servers answered, the appliance's role for the domain) appear as notes under the panel titles. WARN means in place but wrong or weaker than intended, such as an MTA-STS policy in testing mode. Records inside the domain's zone (MX, SPF, DKIM, DMARC, MTA-STS, TLS-RPT, TLSA and the MX hosts' addresses) are read from the domain's own authoritative name servers, found by an NS lookup, so the result is what is published right now with no resolver cache in between; reverse DNS, hosts in other zones and the DNSSEC validation come from public resolvers, and the note under the panel title names the servers asked. The appliance's own resolver is never used, so local overrides cannot distort the result; blocklist lookups go through the appliance's recursive resolver and a lookup that fails is reported as not checked, never as a listing. The website's A record is not part of the checks: mail is delivered to the MX hosts.
| Group | Checks |
|---|---|
| Inbound | MX records (present, host names not addresses or CNAMEs), each MX host resolves, reverse DNS of each MX address matches the host name, SPF (one record, syntax, DNS-lookup count under 10, the MX covered, final -all), DKIM (the selector this appliance signs with is published and matches the local key; other common selectors listed), DMARC (policy, pct, report addresses), MTA-STS (record and policy file, mode), TLS-RPT, DANE TLSA per MX host, DNSSEC. |
| This appliance | A note states the appliance's role for the domain (configured and mail-enabled here with its mailboxes and groups, whether an MX address is one of this appliance's addresses, which selectors sign here); the check lines verify that the DMARC report addresses in the domain are deliverable here. |
| SMTP | Port 25 answers on every MX address, the greeting, STARTTLS with the certificate's subject, issuer, expiry and verification result for the MX name. |
| Blocklists | Every MX address against the appliance's own postscreen zones plus 21 well-known public DNS blocklists (Spamhaus ZEN, Barracuda, Hostkarma, Backscatterer, SpamCop, Mailspike, SEM, SpamRATS, PSBL, SWINOG, GBUdb, UCEPROTECT, s5h, 0spam, Nordspam, DroneBL, blocklist.de, Interserver, SPFBL, anonmails and others), all asked at the same time. Each listing is its own line with the list's own reason text and a Delist button that opens that list's removal page for the address (removal is a self-service form with verification at every operator; nothing is submitted automatically). The panel header carries the totals (listed, clean, no answer); per address the summary line is followed by the listings in full and by one collapsed row that opens to the lists that answered clean and the lists that did not answer. |
Checks that judge a record rather than being one (the SPF lookup count, MX authorisation, final rule and hard fail; the DMARC enforcement, coverage and report lines) appear as indented sub-rows under that record; they inherit its status and level and show a badge only where they deviate (a status other than PASS, or a different level), and the record's Propagation cell spans the group. The Propagation column (DNS panel only) on every record line shows how many of 16 well-known public resolvers around the world (Google, Cloudflare, Quad9, OpenDNS, AdGuard, Yandex, Hurricane Electric, Verisign, NextDNS, Comodo, Dyn, HiNet, 114DNS, Baidu, Freenom) already return the published value, shown as Yes (all answering resolvers return it), Partial (some still hold an older answer, normal right after a change while their caches expire) or No; hovering the cell names the resolvers that still differ. No third-party service is involved: the appliance asks the resolvers directly. The four groups run at the same time and each panel fills as soon as its answer arrives; the DNS panel shows its record lines first and the Propagation column reads Working... until the resolver answers are in. The page remembers, per user, the last domain validated and its finished result: on the next visit that result is shown again with the time it was taken, and nothing runs until Validate is clicked (the validate link on the Domains page runs the checks at once).
Mail Tests
Diagnostics > Mail Tests (/mailtests) sends a well-formed test message from this appliance to an external deliverability tester and shows the verdict. Pick the sender (a mailbox on this appliance; the convention is outbound-smtp-test@<domain>) and the destination. For mail-tester.com the page offers a fresh one-time address (made the way their site makes it) and, once the message is sent, reads the result page every ten seconds for up to four minutes: the score out of ten and every section of the report (SpamAssassin, authentication, formatting, blocklists with the operator's removal link, broken links). The message is a short newsletter-style text with an HTML alternative, List-Unsubscribe (mail and one-click), List-Id and Reply-To; it is injected into this appliance's own Postfix as a local client, so it is DKIM-signed and leaves exactly like customer mail. Any other destination address is simply sent to. Every run is kept in the history with its score, so the effect of a change can be followed over time.
Stats pages
Mail Metrics (/mailmetrics, see above), Statistics (/stats, see above), Trends (/trends:
share of each rspamd outcome across scanned messages), Reports (/reports), Quarantine
Browser (/quarantine: the Dovecot Quarantine folder, list / release / delete through the agent),
Greylist Entries (/greylist).
Diagnostics
| Page | Path |
|---|---|
| Live Mail Log | /maillog |
| Network | /network-settings |
| System Info | /sysinfo |
| Internet Tests | /nettest |
| Email Loopback Test | /loopback |
| Blacklist Check | /blacklist — is this appliance's IP listed on the curated RBL set |
| Replay Corpus | /replay — replay a real spam archive through the appliance on the loopback interface, each message under its original client identity (see below) |
Replay Corpus
Diagnostics > Replay Corpus tests the anti-spam engine with real spam without publishing any address
and without a second machine. A corpus (an archive such as the monthly files of untroubled.org/spam, an mbox,
a Maildir or a directory of messages) is unpacked on the appliance and every message is injected into a dedicated Postfix
listener on 127.0.0.1:10026. That listener bypasses postscreen and accepts XCLIENT from the
loopback, so each message is presented under its original client address and HELO name (from its topmost
public Received header): blocklists, reverse DNS, HELO rules, greylisting, geo scoring and every content rule
judge the real source. The recipient is always a local mailbox (created if missing); spam is rejected at the door or
delivered there, nothing is bounced or forwarded, and only DNS lookups leave the appliance.
| Setting | Meaning |
|---|---|
| Mode local-only | The public blocklists (Spamhaus, SURBL, URIBL, dnswl, SenderScore, Mailspike, SEM, MSBL, blocklist.de, rspamd's own lists) and the rspamd.com fuzzy feed are not queried for the run. Their free tiers are for non-commercial, low-volume use (rspamd.com: under 5,000 queries a day), and a replay of thousands of messages would exceed them. The locally mirrored zones and every content rule still run. Default. |
| Mode full | Everything runs, public lookups included. Keep such runs small and paced. |
| Greylisting and rate limits off | Replayed messages are not deferred with "try again later", so each one gets a final verdict (greylisted messages would otherwise count as deferred). Default on. |
| Rate / sessions / limit | Messages per second, parallel SMTP sessions (capped by the memory of the appliance: the virus scanner and every rspamd worker need their share), and how many messages of the corpus to replay (0 = all). |
| Target mailbox | Where accepted messages land. The mailbox is created with a random password if it does not exist; set a password on the Domains page to read it with a mail client, or use Purge mail on the run to expunge what it delivered. |
The run report shows the SMTP outcome of every message (delivered / deferred / rejected), the rspamd action and score,
the symbols hit most and a score histogram. Runs are kept (last 50) with their per-message results under
/var/lib/mailtronx/replay/; the unpacked corpus is removed after the run unless kept. The replay header
X-MailtronX-Replay marks every replayed message and drives the rspamd settings rules for the two modes.
A message without any public address in its Received headers is presented from the documentation address 192.0.2.1 (neither local nor real) and the host and HELO rules are switched off for it, so only its content is judged. Replayed messages carry their original Date, so the DATE_IN_PAST rule is switched off for them. Postscreen is not exercised by a replay (it does not support XCLIENT); test it with real connections.
Configuration canary
/opt/mailtronx/deploy/canary.py, run as root on the appliance, answers one question after a patch or an installer run: was anything the operator configured lost? seed creates one deliberately named test item on every page that holds items (routing, sender relay, forwardings, BCC, allow relaying, the Anti-Spam lists, a mailbox, a mail group, a quota group, a console account, a domain), through the same forms the pages use, with inert values (.invalid names, TEST-NET addresses, names containing canary). snapshot records every other configured value: the configuration tables and the effective service configuration (Postfix parameters, rspamd thresholds and maps, certificate assignment, time zone and time sources, swap, SSH, logging, backup and patch settings). verify checks every canary item in the database and in the effective configuration (Postfix map lookups, rspamd map files, the relay networks file) and reports every snapshot value that is missing or changed; it exits 1 when anything was lost. clean removes the canary items. State lives under /var/lib/mailtronx/canary/.
Help & Support
Documentation (this page, /documentation), then Contact, Forum, License and Add-on Software pages under
/help/. License states the licence the appliance is released under: the GNU Affero General Public
License, version 3 or later; the name and logo are not covered by it.
Regeneration from the database
The database is the source of truth for settings stored there. Initial deployment and the installer's explicit configuration pass generate the corresponding service files. Ordinary console restarts and application patches do not regenerate configuration, so installing software updates leaves the effective configuration untouched. POST /api/system/render (Admin) explicitly regenerates database-backed settings on demand and returns a per-area report; the audit trail records every run.
Architecture reference
| Component | Detail |
|---|---|
| Theme | The appliance stylesheet (assets/appliance.css) with its named theme navy selected on every page (<html data-theme="dark" data-theme-name="navy">); MailtronX-specific rules live in assets/mailtronx.css and use the theme tokens, never hard-coded fills. |
| Database | MariaDB mailtronx: mt_admins, mt_audit, mt_domains, mt_mailboxes, mt_groups, mt_group_members, mt_lists (all list pages, keyed by list_key), mt_rbl, mt_rbl_stats, mt_msg_stats, mt_outcome_stats, mt_outcome_daily. |
| Logs | /var/log/mail.log (weekly rotation, rename), /var/log/rspamd/rspamd.log (daily rotation). |
| Mail store | /var/vmail on its own disk (role "Mail"). |
API
Every endpoint accepts either the signed-in browser session (mutations then need the csrf field the pages
carry) or an API token (Configure Server > Manage Users > API Tokens) as Authorization: Bearer <token>,
with no CSRF field. Inputs are form-encoded (application/x-www-form-urlencoded); answers are JSON. The token's role
decides what it may change, exactly as for an account. Examples:
curl -H "Authorization: Bearer mtx_..." https://mail.example.com/api/whoami
curl -H "Authorization: Bearer mtx_..." https://mail.example.com/api/metrics
curl -H "Authorization: Bearer mtx_..." -X POST https://mail.example.com/api/certs
curl -H "Authorization: Bearer mtx_..." -X POST --data-urlencode "name=mail" --data-urlencode "cert_pem@cert.pem" \
--data-urlencode "chain_pem@chain.pem" --data-urlencode "key_pem@key.pem" https://mail.example.com/api/certs/import
/api/whoami GET
{"principal": "admin" | "token:<name>", "role": "admin|ops|readonly", "via": "session|token"}./api/metrics GET
cpu_pct, mem_pct, mem_gb, mem_avail, swap_pct, load, rx_bps, tx_bps, time,
disks[] (mount, src, fstype, role, short, used, total, pct), queue_total, queue_active, queue_deferred, queue_hold,
queue_incoming, queue_maildrop, smtp_conns, scans_per_min, scan_window_min, spam_pct, spam_pct_basis, scan_ms, scan_samples,
av_daemon, av_freshclam, av_daily_version, av_daily_built, av_sig_age_h, av_state. No-store./api/diag GET
/api/blk GET
/blacklist./healthz GET
ok. Unauthenticated./favicon.ico GET
/static/favicon.svg and /static/apple-touch-icon.png). Unauthenticated.MailtronX hub submissions
The RBL page shows unique IPs submitted, events acknowledged by the hub, and events waiting to upload. Queued events upload during the five-minute signed-feed sync. Multiple events can refer to the same IP, so event and IP totals differ. The last successful submission changes only when the hub acknowledges an upload; an empty sync does not count as a new submission. Submitted IPs are evaluated against listing policy before entering the blocklist. If aggregate statistics are unavailable, the page says so rather than displaying zero totals.
About this documentation
This page is embedded in the mailtronx-web binary, so it always matches the running version shown in the badge above.
Security policy controls
Administrator policy validation and transaction helpers are included in this release. The SSH, HELO, antivirus and rate-limit configuration forms and SSH confirmation rollback are still in development. Existing policy views and live settings remain in place.
Certificates uses a centered search field to filter by name, common name, SAN or issuer. Expiry filter buttons are removed; expiry dates, days remaining and status indicators remain in the certificate table.
The Certificates panel header shows only Certificates. Service assignments are displayed in the table's Used By column.
During a fresh installation, the first certificate is stored with verified encrypted recovery before the installer configures the TLS services. Automatic activation is deferred only when no managed TLS site or existing service certificate assignment exists; configuration discovery errors still stop the operation.
Matching certificates imported or issued for this appliance are automatically applied to nginx, Postfix and Dovecot after native configuration validation. The Used By column reports actual service assignments; there is no Assign button. New certificates that do not cover all configured appliance service hostnames remain stored without replacing the active certificate. Invalid matching renewals are rejected. Automatic application uses verified encrypted recovery and restores previous settings if validation or reload fails.
Native imported-certificate update (0.25.7): the agent action cert-update accepts replacement certificate, chain and key material for an existing IMPORTED entry. It retains the entry name and assignments and uses the central validation, recovery and activation transaction; it cannot replace an ACME or self-signed entry. Selected native chat certificate copies refresh after the source transaction succeeds. A subsequent chat refresh failure is reported as partial application. This is a native operator action; the Certificates page has no new update control.
Persistent client attackers
Login Abuse Protection can additionally track persistent source addresses. Three distinct temporary bans within 24 hours, or ten confirmed malformed-protocol events within 24 hours, trigger a seven-day block. A qualifying attack after that seven-day block expires triggers a thirty-day block. Duplicate and restored short bans do not create additional strikes. Ordinary disconnects, TLS errors, successful authentication and SMTP delivery errors do not qualify as malformed-protocol evidence.
The existing protection switch and individual IP exemptions also govern persistent protection. Existing policies keep persistent escalation disabled until explicitly enabled. Long blocks apply to mail/chat client ports only; SSH, admin web access and server-to-server SMTP port 25 are excluded. This local policy does not publish an address to a public RBL. The agent collects evidence without an AI model and restores unexpired blocks after a restart.
The Security Events page shows collector health, last scan, active long blocks and bounded recent block history. Recorded evidence contains source IP, event category, timestamps and counts, without passwords, message contents or raw usernames. An administrator can unblock an individual address using the existing audited, CSRF-protected action. This removes active local bans and resets that address's qualifying history so old events do not immediately reblock it. Subsequent qualifying attacks can trigger protection again. A misconfigured legitimate client may trigger repeated failed-login bans; review the evidence before adding an individual-device exemption.
Logging → Security Events
The Logging navigation group contains MAILLOG2, Mail Log and the dedicated Security Events view at /security-events. It shows the available event progression across the server by default: retained native temporary restrictions, normalized malformed-protocol events, seven-day/thirty-day long blocks and release transitions. Filter an individual address using the IP field or /security-events?ip=192.0.2.1. MAILLOG2 client IP links open this filtered view. IP columns remain on one line and wide tables scroll within their panel.
GET /api/security-events returns up to the latest 400 available events in chronological order, optionally filtered with ?ip=.... Native Fail2ban history is limited by its retention. Normalized capture does not represent every failed login or activity before activation. Missing durations are UNKNOWN; permanent bans are labeled Permanent. A native history warning leaves available normalized events visible. Historical records describe past restrictions and do not by themselves claim the address is currently blocked.
Active managed mail-client bans and read-only bans from other jails appear below the event log. Only administrators can use the existing audited, CSRF-protected mail-client unblock action; it does not change SSH or other external jail bans. Protection settings remain separate under Configure Server → Login Abuse Protection at /login-abuse, with a link to the Security Events view.
Cluster topology and mail flow
Cluster Management shows a compact map directly below the page description. It includes recorded local, enrolled, backend and DNS nodes, their known hostnames and IP addresses, scoped health indicators and last observation time. Select a node to inspect its inventory role, addresses, status reason, domains, MX preferences and route sources. The Connection evidence disclosure provides the same directed relationships as text.
Solid green arrows represent configured delivery routes. Dashed blue arrows represent observed DNS MX advertisements and their numeric preferences. Dotted gray arrows represent directory synchronization, which does not prove message delivery. DNS preference and similar hostnames never create a backend route. A standalone server remains visible even when management is uninitialized or no verified delivery path is recorded. Unknown, stale and unreachable observations are labeled explicitly; directory acknowledgement is not a mail delivery test.
The authenticated read-only GET /api/cluster/topology view loads asynchronously and refreshes while visible every 30 seconds. It does not enroll servers, change routes or send test mail. Loading or topology lookup errors leave cluster configuration available; a previously recorded map remains visibly qualified as potentially stale. The diagram scrolls within its panel on small screens.
Greylisting controls and shared activity
Anti-Spam → Greylisting uses the MailtronX defaults: score 4, retry delay 5 minutes and record lifetime 24 hours. The GeoIP country option shows recommended/default labels and actual selected-country coverage. These controls preserve existing security inspection and relay policy.
Recent temporary rejections shows durable historical receiving-side Postscreen, Postfix and Rspamd events (outbound queue delivery failures are excluded) from the enrolled cluster, including the originating server, component, timestamp, observed sender/recipient/IP, reason and available score/SMTP response. Filter by origin, component, time or text. Explicit greylist evidence is distinguished from other temporary failures. The table does not prove current waiting state, retry eligibility or final delivery.
Each appliance captures bounded batches from local retained logs, saves event records and offsets atomically, and synchronizes through the existing authenticated cluster authority. Acknowledged uploads and incremental downloads tolerate replay and outages; replicas serve cached events with visible collection/synchronization warnings and per-origin freshness. Known upload/download backlogs are shown as catching up, even when the latest exchange succeeded. Long observed fields are bounded and marked as truncated. Directory credentials require the explicit gateway-events capability for event access; revocation applies to both. No event data goes to public reputation services. Synchronization does not modify filtering rules or greylist timers. Rspamd evidence is collected from its native rspamd.log family only; duplicate syslog copies are excluded, and an unavailable native log is reported as missing coverage. Collection is limited to available retained logs; unavailable sources, ambiguous rotation and lost unread rotations are reported as gaps. Storage is bounded to 100,000 events and 256 retained or unresolved source generations per appliance; at capacity collection stops with an explicit warning instead of deleting history or advancing offsets. Completed derived offsets are retired only after their source leaves retained logs; unresolved gaps are preserved. Compressed catch-up streams past saved offsets and may need extra CPU for large rotations. No event deletion or new retention policy is introduced.