# GoMail A complete self-hosted email server in a single Go binary — replacing postfix + dovecot + roundcube + spamassassin + radicale with one process, hand-rolled almost entirely on the Go standard library (SMTP, IMAP, POP3, JMAP, CalDAV/CardDAV, ManageSieve, DKIM, SPF/DMARC verification, OAuth2, ACME, JWT — all implemented from scratch, not wrapped around third-party protocol libraries). ## What's included - **Mail transport**: SMTP (inbound MTA + authenticated submission), full security pipeline (SPF, DKIM, DMARC, header/URL heuristics, optional ClamAV/Rspamd/LLM stages), outbound queue with retry and bounce handling - **Mail access**: IMAP4rev1, POP3 (off by default), JMAP (RFC 8620/8621 subset) - **Filtering**: Sieve scripts (RFC 5228 subset) managed via ManageSieve (RFC 5804) - **Calendar & contacts**: CalDAV/CardDAV, per-user and per-tenant - **Webmail**: built-in dark-themed web client — folders, compose, search, quarantine review, MFA/app-password/recovery settings - **Admin portal**: tenants, domains (with DKIM key generation/rotation), users, list rules, outbound queue management, global quarantine - **Multi-account**: link external Gmail/Microsoft 365/generic IMAP accounts via OAuth2 - **Security**: TOTP MFA with backup codes, app passwords, TLS via real ACME (Let's Encrypt-compatible) certificate issuance and auto-renewal, per-IP rate limiting on every listener - **Everything encrypted at rest**: messages, contacts, calendar events, DKIM/TLS/OAuth2 credentials — AES-256-GCM with a per-record key derived via HKDF from a single master key See `gomail-action-plan-v4.md` (if included alongside this README) for a detailed phase-by-phase account of what's built, what's tested, and what's intentionally deferred. ## Quick start ```bash # 1. Generate a master key (required — GoMail refuses to start without one) gomail -gen-master-key # prints: GOMAIL_MASTER_KEY=<64 hex chars> # 2. Set required environment variables export GOMAIL_MASTER_KEY= export GOMAIL_JWT_SECRET=$(openssl rand -hex 32) export GOMAIL_ADMIN_INIT_PASSWORD='choose-a-strong-password' # 3. First run auto-generates a default config at the path you specify gomail -config /etc/gomail/gomail.yaml # 4. Edit /etc/gomail/gomail.yaml before running for real: # - server.hostname: your actual mail server hostname # - tls.mode: "acme" with tls.acme_domains set (see below), or "file" # - tls.acme_email: an address you actually monitor (Let's Encrypt # account contact, used for expiry warnings) ``` For a full system install (dedicated user, systemd unit, directory permissions), use `install.sh` instead of running the binary directly — see that script's header comment for exactly what it does and doesn't do. ## TLS / ACME GoMail obtains real Let's Encrypt-compatible certificates automatically when `tls.mode: acme` and `tls.acme_domains` are set: ```yaml tls: mode: acme acme_email: postmaster@yourdomain.com acme_domains: ["mail.yourdomain.com"] # acme_directory_url defaults to real Let's Encrypt production — # override to Let's Encrypt staging while testing (see below) ``` This requires port 80 to be reachable from the internet for HTTP-01 challenge validation — GoMail runs its own minimal HTTP server on `:80` for this, it doesn't need a separate web server in front of it for the challenge itself. **Before pointing this at Let's Encrypt production, test against staging** — set `tls.acme_directory_url` to `https://acme-staging-v02.api.letsencrypt.org/directory` — to avoid hitting production rate limits while testing. Staging certificates aren't trusted by real browsers/clients, so switch back to the production directory (or just remove the override) once you've confirmed issuance works. Without `tls.acme_domains` set, GoMail falls back to a self-signed certificate — fine for local testing, **not suitable for production**. ## DNS setup Once GoMail is running, publish these DNS records for `yourdomain.com` (adjust names/values to match your actual domain and what GoMail logs at bootstrap — the exact DKIM public key is generated per-installation and logged once at first startup, and also viewable via the admin portal's Domains page). ### MX record Point mail delivery at your GoMail server: | Type | Name | Value | Priority | |---|---|---|---| | MX | `yourdomain.com` | `mail.yourdomain.com` | 10 | Plus an A/AAAA record for `mail.yourdomain.com` pointing at the server's IP. ### SPF record Authorizes your server to send mail for the domain: | Type | Name | Value | |---|---|---| | TXT | `yourdomain.com` | `v=spf1 mx ~all` | `mx` covers "any host listed in this domain's MX records may send mail" — sufficient for a single GoMail instance handling its own outbound delivery. `~all` (soft fail) is a reasonable starting point; tighten to `-all` (hard fail) once you're confident no other source legitimately sends mail as this domain. ### DKIM record GoMail generates a DKIM key pair automatically for each domain at bootstrap (or when created via the admin portal) and logs the exact TXT record to publish — it looks like this: | Type | Name | Value | |---|---|---| | TXT | `mail._domainkey.yourdomain.com` | `v=DKIM1; k=rsa; p=` | The selector (`mail` by default) and the actual key value are domain-specific — copy them from the startup log or the admin portal rather than this example. Rotating the key (also available in the admin portal) requires updating this record to match the new key. ### DMARC record Tells receiving servers what to do with mail that fails SPF/DKIM alignment, and where to send aggregate reports: | Type | Name | Value | |---|---|---| | TXT | `_dmarc.yourdomain.com` | `v=DMARC1; p=quarantine; rua=mailto:postmaster@yourdomain.com` | Start with `p=quarantine` (suspicious mail gets flagged, not silently dropped) rather than `p=reject` until you've confirmed legitimate mail flows aren't being caught by DMARC alignment failures. ### MTA-STS and TLS-RPT **Not yet implemented** by GoMail (see the action plan's Phase 13 deferred-items list) — no records to publish for these yet. This section will be filled in once that work lands. ## Configuration reference Full config lives in `gomail.yaml` (auto-generated with defaults on first run) plus a small number of required environment variables for secrets (`GOMAIL_MASTER_KEY`, `GOMAIL_JWT_SECRET`, optionally `GOMAIL_ADMIN_INIT_PASSWORD`) — secrets are deliberately never stored in the YAML file itself. See `internal/config/config.go` for the full set of available fields and their defaults; every field has a sensible default except the two required secrets above. ## Building from source ```bash go build -o gomail ./cmd/gomail ``` Requires CGO (for `mattn/go-sqlite3`) — a working C compiler must be available. No other build-time dependencies beyond what `go mod download` fetches. ## License (Not yet specified — add your chosen license here before public release.)