Overview
All configuration is stored in config.json at the working directory.
Generate a default file with:
python run.py init
The server reads config.json at startup. A SIGHUP signal
triggers a live reload without restarting listeners.
If
config.json is absent at startup, default values are written automatically.
The default hostname is mail.example.com — always set this to your actual FQDN.
Top-Level Settings
| Key | Default | Description |
|---|---|---|
hostname | mail.example.com | Server FQDN, used in SMTP banners and autoconfig responses. |
mail_root | ./mailstore | Root directory for all user data and TLS certificates. |
domains | ["example.com"] | List of accepted mail domains (case-insensitive at runtime). |
log_level | INFO | Python logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
SMTP Settings (smtp.*)
| Key | Default | Description |
|---|---|---|
smtp.host | 0.0.0.0 | Bind address for all SMTP listeners. |
smtp.port | 25 | SMTP port for inbound delivery (unauthenticated external relay accepted). |
smtp.submission_port | 587 | Authenticated submission port (STARTTLS required for AUTH when TLS configured). |
smtp.smtps_port | 465 | Implicit TLS SMTP port (activated when TLS is configured). |
smtp.max_message_size | 52428800 | Maximum message size in bytes (50 MB). Enforced via SIZE extension. |
smtp.max_recipients | 50 | Maximum RCPT TO recipients per transaction. Returns 452 on excess. |
smtp.max_connections | 100 | Maximum simultaneous SMTP connections. |
smtp.max_messages_per_connection | 50 | Maximum messages per SMTP session before disconnect. |
IMAP Settings (imap.*)
| Key | Default | Description |
|---|---|---|
imap.host | 0.0.0.0 | Bind address for IMAP listeners. |
imap.port | 143 | IMAP4rev1 plain-text port (STARTTLS available when TLS configured). |
imap.imaps_port | 993 | IMAPS implicit TLS port (activated when TLS is configured). |
imap.max_connections | 100 | Maximum simultaneous IMAP connections. |
Exchange API Settings (exchange_api.*)
| Key | Default | Description |
|---|---|---|
exchange_api.host | 0.0.0.0 | Bind address for HTTP API listeners. |
exchange_api.port | 9002 | HTTP API port. |
exchange_api.secure_port | 9003 | HTTPS API port (activated when TLS is configured). |
exchange_api.token_secret | auto-generated | HMAC-SHA256 key for JWT signing. Set explicitly for multi-process deployments so tokens are valid across processes. |
TLS Settings (tls.*)
| Key | Default | Description |
|---|---|---|
tls.cert_file | null | Path to PEM certificate. When null, a self-signed cert is auto-generated in {mail_root}/tls/cert.pem. |
tls.key_file | null | Path to PEM private key. When null, auto-generated alongside the cert. |
When TLS is configured (cert_file is set or auto-generated), AUTH is hidden from cleartext
SMTP EHLO and IMAP CAPABILITY responses. Clients must negotiate STARTTLS before
authentication credentials are accepted. This behaviour is intentional and cannot be
disabled independently of TLS.
Auto-generated certificates are self-signed and suitable for testing or internal use only.
For production, set
tls.cert_file and tls.key_file
to paths of CA-signed certificates before starting the server.
Delivery Settings (delivery.*)
| Key | Default | Description |
|---|---|---|
delivery.workers | 4 | Number of async worker coroutines in the delivery queue. Increase for high-volume inbound workloads. |
Rate Limiting (rate_limit.*)
Per-IP sliding-window rate limiting applied to SMTP connections. Enabled by default.
| Key | Default | Description |
|---|---|---|
rate_limit.enabled | true | Enable or disable rate limiting globally. |
rate_limit.connection_rate | 20 | Maximum connections per IP within connection_period. |
rate_limit.connection_period | 60 | Period in seconds for connection rate window. |
rate_limit.message_rate | 30 | Maximum messages per IP within message_period. |
rate_limit.message_period | 60 | Period in seconds for message rate window. |
rate_limit.recipient_rate | 100 | Maximum recipients per IP within recipient_period. |
rate_limit.recipient_period | 60 | Period in seconds for recipient rate window. |
rate_limit.auth_failure_limit | 5 | Authentication failures before IP lockout. |
rate_limit.auth_failure_lockout | 600 | Lockout duration in seconds after auth failure limit is reached. |
rate_limit.max_concurrent_per_ip | 10 | Maximum simultaneous connections from a single IP. |
rate_limit.whitelist | ["127.0.0.1", "::1"] | IPs exempt from all rate limiting. |
rate_limit.max_tracked_ips | 10000 | Maximum number of IPs tracked in memory. |
rate_limit.cleanup_interval | 300 | Seconds between expiry cleanup of stale IP records. |
Ensure
127.0.0.1 remains in rate_limit.whitelist when running
diagnostics or tests from localhost, otherwise test connections will be rate-limited and
the diagnostic suite will fail.
Example config.json
{
"hostname": "mail.example.com",
"mail_root": "./mailstore",
"domains": ["example.com"],
"log_level": "INFO",
"smtp": {
"host": "0.0.0.0",
"port": 25,
"submission_port": 587,
"smtps_port": 465,
"max_message_size": 52428800,
"max_recipients": 50,
"max_connections": 100,
"max_messages_per_connection": 50
},
"imap": {
"host": "0.0.0.0",
"port": 143,
"imaps_port": 993,
"max_connections": 100
},
"exchange_api": {
"host": "0.0.0.0",
"port": 9002,
"secure_port": 9003,
"token_secret": null
},
"delivery": {
"workers": 4
},
"tls": {
"cert_file": null,
"key_file": null
},
"rate_limit": {
"enabled": true,
"connection_rate": 20,
"connection_period": 60,
"message_rate": 30,
"message_period": 60,
"auth_failure_limit": 5,
"auth_failure_lockout": 600,
"max_concurrent_per_ip": 10,
"whitelist": ["127.0.0.1", "::1"]
}
}