Files
gomail/README.md
T

178 lines
6.9 KiB
Markdown
Raw Normal View History

2026-08-09 18:03:09 +01:00
# 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=<paste from step 1>
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=<base64 public key>` |
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.)