The Mailezine Engine
Mailezine is Mailez's first-party mail engine: a single binary with pluggable storage. It is the only engine across every tier (dev / community / enterprise) — protocol capabilities and mail features are identical between editions; the editions differ only in storage scale and licensed features.
Relationship Between Editions
| Community | Enterprise | |
|---|---|---|
| Engine | Mailezine (the same binary) | Mailezine (the same binary) |
| Storage | Pebble KV + local-FS blob (single node) | TiDB KV + MinIO/S3 blob (distributed) |
| Gateway | nginx for HTTP/ACME only | same |
| Mail ports | Published by the engine, built-in implicit TLS (465/993/995) | same |
| Platforms | Native Linux / macOS / Windows | same |
| Scaling | — | Object-storage elasticity, multi-instance HA, TiDB horizontal scaling |
Both editions share the same backend control plane (directory / auth / admin
console / Webmail). Traditional Postfix + Dovecot deployments migrate in place
with mailezine migrate.
Design Principles
- Single binary — one executable contains everything
- Pluggable storage — pebble (local / single node) / tidb (distributed) + S3/MinIO blob
- All protocols in-house — SMTP / IMAP / POP3 / ManageSieve, with MIT libraries such as go-msgauth
- One engine — community and enterprise run the same code; upgrades only touch storage and licensing
Current Status
Completed:
- ✅ SMTP send/receive / validation / spam filtering / local delivery / outbound queue (incl. SRS, rate limiting)
- ✅ IMAP read path (CONDSTORE / SORT / ACL / ID / QUOTA)
- ✅ POP3 / ManageSieve / Sieve executor
- ✅ Pebble / TiDB KV + MinIO/S3 blob storage loop
- ✅ Full-text search (bleve) + Tika attachment text extraction (PDF / Office / ODF)
- ✅ Two clustering shapes (enterprise): active-passive lease failover, or the
multitier's fully multi-active engine — every replica serves every account; per-message transactional delivery claims with crash-takeover, cross-node singleton leases, per-node FTS convergence and per-account write pinning, verified by a real two-process kill -9 failover drill; implicit TLS - ✅ Archive capture and outbound DLP approval (wired to the control plane)
- ✅ One-way traditional-architecture (Postfix+Dovecot / Maildir) → Pebble/TiDB migration tool
- ✅ Full test suite green on all three platforms (Linux/macOS/Windows)
Benchmarks (10,000-user enterprise load)
Measured on the same machine in the same session (September 2026 re-run); the "traditional stack" column is the Postfix + Dovecot architecture running side by side, each stack paired with its control plane:
| Metric | Traditional stack (postdove) | Mailezine | Gain | What it means |
|---|---|---|---|---|
| Inbound throughput | 3.1 msg/s | 13.6 msg/s | 4.4× | 3.4× more mail per second on the same hardware |
| Inbound p50 | 6.19 s | 1.35 s | 4.6× | New mail arrives near-instantly |
| Submission throughput | 2.2 msg/s | 6.8 msg/s | 3.1× | Bulk outbound queues drain comfortably |
| IMAP FETCH p50 | 35.97 ms | 20.96 ms | 1.7× | Snappier reads; long lists scroll smoothly |
| Queue delivery | 2.1 msg/s | 7.2 msg/s | 3.4× | Fewer bounces and delayed deliveries |
| Enqueue→delivery p50 | 35.6 s | 5.30 s | 6.7× | Nearly seven times faster end-to-end delivery |
| Queue backlog | 122 msgs | 0 msgs | zero | Zero backlog under load |
| Idle memory (full stack) | ~162 MiB | ~6.4 MiB | 1/25 | Runs comfortably on small hosts |
| Peak load memory | ~270 MiB | ~73 MiB | 1/3.7 | Memory stays flat through mail floods |
Standalone Development
# 1. Prepare the dev directory and password stubs
$env:MAILEZINE_DIRECTORY_FILE = "internal/directory/testdata/dev-directory.json"
$env:MAILEZINE_AUTH_DEV_FILE = "internal/auth/testdata/dev-passwords.json"
# 2. Start (listens on :11480 health endpoint by default)
go run ./cmd/mailezine
# Also listens on :1025 (SMTP inbound), :1587 (Submission), :1143 (IMAP),
# :10110 (POP3), :11490 (ManageSieve)
# 3. Verify
curl http://127.0.0.1:11480/health
# 4. End-to-end smoke test (local delivery + relay enqueue)
go test ./cmd/mailezine -run TestEndToEnd -v
Configuration
Everything can be overridden via environment variables (or a TOML overlay
file via MAILEZINE_CONFIG):
| Variable | Default | Description |
|---|---|---|
MAILEZINE_STORAGE_BACKEND | pebble | pebble | tidb |
MAILEZINE_ROCKS_PATH | — | Pebble KV path (required) |
MAILEZINE_STORAGE_DSN | — | TiDB DSN (required for tidb) |
MAILEZINE_S3_ENDPOINT | empty | S3/MinIO blob; empty = local FS |
MAILEZINE_S3_ACCESS_KEY / MAILEZINE_S3_SECRET_KEY / MAILEZINE_S3_BUCKET | — | S3 credentials and bucket |
MAILEZINE_BLOB_COMPRESSION | false | Compress blobs at rest (text compresses 60–80%) |
MAILEZINE_FTS_ENABLED | false | Enable bleve full-text search |
MAILEZINE_FTS_TIKA_URL | empty | e.g. http://tika:9998; enables attachment text extraction |
MAILEZINE_HA_ENABLED | false | Multi-instance active-passive HA |
MAILEZINE_HA_LEASE_PATH | empty | Shared lease file; empty = S3 bucket lease |
MAILEZINE_CLUSTER_MODE | single | multi = fully multi-active engine (requires TiDB; mutually exclusive with HA) |
MAILEZINE_CLUSTER_NODE_ID | auto | Multi-active node identity (leave empty under compose replicas) |
MAILEZINE_QUEUE_CLAIM_LEASE_SECONDS | 600 | Multi-active delivery-claim lease (must exceed the slowest single delivery) |
MAILEZINE_ACCOUNT_GATE_ENABLED | true | Multi-active per-account write pinning (advisory; proceeds after the bounded wait) |
MAILEZINE_ARCHIVE_ENABLED / MAILEZINE_ARCHIVE_URL | false / empty | Archive capture, forwarded to the control-plane archive store |
MAILEZINE_DLP_ENABLED / MAILEZINE_DLP_URL | false / empty | Outbound DLP scan + approval; fails open |
MAILEZ_LICENSE_FILE | empty | Enterprise license file (platform-level; unset = development edition, unlimited mailboxes) |
MAILEZ_LICENSE_REQUIRED | false | When true, a missing or invalid license refuses startup |
MAILEZINE_DIRECTORY_FILE / MAILEZINE_AUTH_DEV_FILE | — | Dev-mode directory / password stubs |
MAILEZINE_BACKEND_ADDRESS | 127.0.0.1:8080 | Backend control-plane address |
MAILEZINE_STACK_SECRET | empty | Shared secret for the control-plane /stack API |
MAILEZINE_HOSTNAME | hostname | EHLO / Authentication-Results hostname |
MAILEZINE_RSPAMD_URL | empty | Enterprise high-tier anti-spam: e.g. http://mail-filter:11333/checkv2; takes precedence over the built-in baseline when set |
MAILEZINE_JUNK_ENABLED | true | Built-in baseline anti-spam (community default tier): auth-results scoring + DNSBL + sender lists; yields automatically when Rspamd is configured |
MAILEZINE_JUNK_HEADER_SCORE / MAILEZINE_JUNK_REJECT_SCORE | 4.5 / 12 | Score thresholds for X-Spam headers / MTA rejection (deliberately lenient) |
MAILEZINE_JUNK_RBLS | bl.spamcop.net | Comma-separated DNSBL zones; empty disables DNSBL queries |
MAILEZINE_JUNK_WHITELIST / MAILEZINE_JUNK_BLACKLIST | empty | Full addresses, bare domains or @domain, comma-separated |
MAILEZINE_JUNK_GREYLIST | false | Greylist first-seen senders scoring in the ambiguous band (retries within an hour pass) |
MAILEZINE_SMTPS_ADDR / MAILEZINE_IMAPS_ADDR / MAILEZINE_POP3S_ADDR | empty | Implicit-TLS ports (requires certificates) |
Migration Tool
To migrate a traditional architecture (Dovecot Maildir storage) into KV storage,
use mailezine migrate:
# maildir → pebble (local KV)
go run ./cmd/mailezine migrate --src /var/vmail --dst /var/lib/mailezine
# maildir → tidb (distributed KV, optional S3 blob)
go run ./cmd/mailezine migrate --src /var/vmail --to tidb --dsn "root:@tcp(127.0.0.1:4000)/mailezine"
Migration preserves per-mailbox order, flags, keywords and internal dates;
UIDs/UIDVALIDITY are re-allocated per store invariants and clients resync
per RFC 3501. --dry-run scans and counts without writing. The maildir
reader is POSIX-only; other platforms get a stub that explains the constraint.
Licensing (Enterprise)
The enterprise edition is controlled by an offline signed
license file (Ed25519). The license is a perpetual software right: it
carries edition (enterprise) and max_mailboxes (the mailbox/user cap,
enforced forever) with no expiry. Without a license it runs as the
development edition (unlimited). Production deployments mount the license
file and set MAILEZINE_LICENSE_REQUIRED=true; a missing or invalid license
refuses startup. Licenses are issued by Mailez HQ (cmd/license), and the
admin console enforces the same mailbox cap when creating mailboxes.
Annual technical service (support, SLA, launch assistance) is a separate
subscription decoupled from the license, loaded via MAILEZ_SERVICE_FILE
(issued with cmd/license service issue). Service certificates can be
mounted by both the community and enterprise editions — they represent
support entitlement, never software gating.