2026-05-21 16:43:41 +00:00
2026-05-21 20:27:58 +00:00
2026-05-21 20:27:58 +00:00
2026-05-21 17:42:17 +01:00
2026-05-21 20:27:58 +00:00
2026-05-22 06:06:44 +00:00
2026-05-22 06:06:44 +00:00

mailgosend

Self-hosted email server and webmail client in a single Go binary.

What it does

  • SMTP — receives inbound mail (port 25), accepts authenticated submission (587 STARTTLS, 465 TLS)
  • IMAP — serves mail to desktop clients (143 STARTTLS, 993 TLS)
  • Webmail — full browser client: read, compose, reply, forward, trash, search (port 8080)
  • Admin panel — manage domains, users, queue, IP bans, security events (port 8081, localhost-only by default)
  • CalDAV — calendar sync compatible with Apple Calendar, Thunderbird, DAVx5 (port 8080 at /caldav/)
  • CardDAV — contacts sync compatible with Apple Contacts, Thunderbird, DAVx5 (port 8080 at /carddav/)
  • DKIM — signs outbound mail, verifies inbound signatures
  • SPF / DMARC — validates inbound mail policy
  • Spam filtering — DNSBL checks + header heuristics with configurable score threshold
  • Encryption at rest — all email bodies, attachments, contacts, and calendar events encrypted AES-256-GCM
  • TOTP / MFA — per-user two-factor authentication with backup recovery codes
  • Auto TLS — ACME (Let's Encrypt) via DNS-01 or HTTP-01; or bring your own certs
  • Multi-domain — serve multiple mail domains from one instance

Requirements

  • Go 1.26.3+
  • Linux / macOS (Windows: SMTP port 25 typically blocked)
  • Ports 25, 587, 465 open inbound for mail reception
  • A real FQDN with MX, A/AAAA, and SPF DNS records

Build

git clone https://ghb.freebede.com/nahakubuilder/mailgosend
cd mailgosend
go build -o mailgosend ./cmd/mailgosend/

Produces a single ~35 MB binary with all web assets embedded.


First run

./mailgosend

On first run with no app_config.conf present, the binary:

  1. Generates app_config.conf with secure random ENCRYPTION_KEY and SESSION_SECRET
  2. Creates ./data/mail.db (SQLite) and applies the schema
  3. Starts all configured services and prints listening addresses

Back up app_config.conf immediately — the encryption key is required to read stored mail. Losing it means losing access to all stored messages, contacts, and calendar data.


Configuration

All settings live in app_config.conf (INI-style KEY = VALUE). The file is auto-generated with commented defaults on first run. The most important settings:

Identity

Key Default Description
HOSTNAME mail.example.com Server FQDN — used in SMTP HELO and TLS SNI
DEFAULT_DOMAIN example.com Primary mail domain

Ports

Key Default Description
SMTP_PORT 25 Inbound SMTP
SUBMIT_PORT 587 SMTP submission (STARTTLS)
SMTPS_PORT 465 SMTP submission (implicit TLS)
IMAP_PORT 143 IMAP (STARTTLS)
IMAPS_PORT 993 IMAP (implicit TLS)
WEBCLIENT_PORT 8080 Webmail + CalDAV + CardDAV
WEBADMIN_PORT 8081 Admin panel (binds 127.0.0.1 by default)

Disable any service by setting its _ENABLED = false or _PORT = 0.

TLS

Key Default Description
TLS_MODE dns01 dns01 | http01 | file | off
ACME_EMAIL (empty) Required for ACME modes
ACME_DNS_PROVIDER cloudflare cloudflare | route53 | digitalocean | hetzner
TLS_CERT / TLS_KEY ./certs/ Certificate paths for TLS_MODE=file

For dns01 (recommended for wildcard certs), set the provider credentials:

TLS_MODE         = dns01
ACME_EMAIL       = admin@example.com
ACME_DNS_PROVIDER = cloudflare
CF_DNS_API_TOKEN = your-cloudflare-api-token

For manual / existing certs:

TLS_MODE = file
TLS_CERT = /etc/letsencrypt/live/mail.example.com/fullchain.pem
TLS_KEY  = /etc/letsencrypt/live/mail.example.com/privkey.pem

Storage

Key Default Description
DB_DRIVER sqlite sqlite (embedded, no deps)
DB_PATH ./data/mail.db SQLite database file
STORAGE_BACKEND db db (blobs in SQLite) | fs (files on disk)
STORAGE_FS_PATH ./data/messages Directory for fs backend
MAX_MESSAGE_SIZE 52428800 Max message size in bytes (50 MB)

Security

Key Default Description
BRUTE_MAX_TRIES 5 Failed attempts before IP ban
BRUTE_WINDOW_MIN 30 Rolling window for attempt counting (minutes)
BRUTE_BAN_HOURS 24 How long a banned IP stays banned
SECURE_COOKIE false Set true when serving over HTTPS (marks cookies Secure)
SESSION_MAX_AGE 604800 Session lifetime in seconds (7 days)

Spam

Key Default Description
SPAM_THRESHOLD 10 Score at which mail is marked spam
SPAM_DNSBL zen.spamhaus.org,... Comma-separated DNSBL hosts
SPAM_CHECK_SPF true Validate SPF on inbound
SPAM_CHECK_DKIM true Validate DKIM on inbound
SPAM_CHECK_DMARC true Validate DMARC on inbound

Queue / Delivery

Key Default Description
QUEUE_MAX_AGE_HOURS 72 Hours before undeliverable mail bounces
QUEUE_RETRY_MINS 5,15,60,240,480 Retry backoff schedule (minutes)
DNS_PRIMARY 1.1.1.1 Primary resolver for MX lookups

DNS records (required)

Replace example.com and 203.0.113.1 with your domain and server IP.

; MX
example.com.           MX  10  mail.example.com.

; A record for the mail host
mail.example.com.      A       203.0.113.1

; SPF — only this server sends mail for example.com
example.com.           TXT     "v=spf1 mx ~all"

; DMARC
_dmarc.example.com.    TXT     "v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com"

; PTR (set at your hosting provider — must match HOSTNAME)
1.113.0.203.in-addr.arpa. PTR mail.example.com.

DKIM keys are generated per-domain from the admin panel. After generating, copy the displayed TXT record into DNS.


Initial setup workflow

  1. Start the binary, open the admin panel at http://127.0.0.1:8081/admin/
  2. On first run there is no admin account. Create one:
    # The binary exposes no separate CLI; create the first admin via the
    # setup endpoint that appears when zero users exist, or insert directly:
    ./mailgosend --create-admin admin@example.com
    
    (If no --create-admin flag exists yet, use the DB directly or the admin panel bootstrap page.)
  3. Add your domain: Admin → Domains → New Domain
  4. Generate DKIM keys: Domain detail page → Generate DKIM
  5. Copy the DKIM TXT record into your DNS provider
  6. Create users: Admin → Users → New User
  7. Point your mail client at the server (IMAP + SMTP credentials = email + password)

Connecting a mail client

Setting Value
IMAP server mail.example.com
IMAP port 993 (TLS) or 143 (STARTTLS)
SMTP server mail.example.com
SMTP port 465 (TLS) or 587 (STARTTLS)
Username Full email address (user@example.com)
Password Account password
Authentication Normal password

CalDAV / CardDAV

Clients discover the service via well-known URLs:

URL Service
https://mail.example.com/.well-known/caldav Calendar discovery
https://mail.example.com/.well-known/carddav Contacts discovery

Authentication: HTTP Basic Auth (email + password). Compatible with Apple Calendar, Apple Contacts, Thunderbird (with TbSync), and DAVx5 on Android.


Two-factor authentication (TOTP)

Users enable MFA from the webmail Settings page. The flow:

  1. Settings → Set Up Two-Factor Auth → scan QR code with authenticator app
  2. Enter the 6-digit code to confirm → MFA enabled
  3. 10 single-use recovery codes are generated and encrypted; displayed once at enrollment time

On next login, users enter password then a 6-digit TOTP code (or 8-char recovery code).

Admin accounts support the same MFA flow via the admin panel login.


Reloading TLS certificates

Send SIGHUP to reload certificates without downtime:

kill -HUP $(pidof mailgosend)

Graceful shutdown

SIGTERM or SIGINT (Ctrl+C) triggers graceful shutdown: HTTP servers drain active connections (10-second deadline), queue worker stops, database closes.


Security model

  • Sessions: raw token in HttpOnly cookie; SHA-256 hash stored in DB. Cookie theft without DB access yields nothing.
  • Passwords: bcrypt (cost 12).
  • Encryption: AES-256-GCM with HKDF-derived per-user per-purpose keys. Master key in app_config.conf.
  • CSRF: stateless HMAC-SHA256 token bound to session + clock hour. No DB storage.
  • Brute force: failed attempts tracked per IP; configurable lockout threshold and duration.
  • Rate limiting: token-bucket per IP — 60 req/min (webmail), 10 req/min (admin).
  • Security headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy on all HTTP responses.
  • HTML emails: rendered in <iframe sandbox="allow-same-origin"> — scripts cannot execute.
  • Admin panel: binds 127.0.0.1 by default; not exposed to the internet.

Logging

Logs go to stdout by default. Set LOG_FILE = ./logs/mail.log to write to a file. Rotate with logrotate + SIGHUP.


Upgrading

Replace the binary and restart. Schema migrations run automatically on startup; they are sequential and non-destructive (only ADD COLUMN and CREATE TABLE IF NOT EXISTS).


License

See LICENSE.

S
Description
No description provided
Readme AGPL-3.0 18 MiB
Languages
Go 83.9%
HTML 16.1%