Apple's iCloud Mail rules are basically useless — a single condition, a single action, and no way to chain anything. Combined with a web client they haven't meaningfully updated in years, managing iCloud mail beyond the basics is a dead end.
Mailflow fixes that. It connects to iCloud via IMAP, runs your incoming mail through a real rules engine (AND/OR logic, 13 condition operators, multiple actions), and handles it all automatically. Everything Apple should have built, running on your own machine.
- IMAP Rules Engine — match messages by from/to/cc/subject/body/headers/attachment/content_type with AND/OR logic and 13 operators, execute actions (move, mark read/unread, set/remove flags, auto_reply, forward, delete, webhook)
- Nested Condition Groups — nest AND/OR groups, e.g.
(from contains @a.com OR subject starts_with "Invoice") AND has_attachment; preserved through export/backup/MCP - Failure Alerts — POST a webhook when the poller goes unhealthy or recovers, or when a scheduled backup fails
- Activity Re-run — dry-run the current rules against any logged message to see which rule matches
- Auto-Reply Throttling — each sender receives at most one
auto_replyper day, with self-address skipping to prevent mail loops - Rule Scheduling — optional time-of-day and day-of-week filter per rule
- Regex Capture — named groups from
matches_regexconditions become[capture:name]template variables - Auto-Reply Templating —
[subject],[from],[date],[to],[cc],[rule_name], and[capture:name]variables - Webhook Action — POST JSON notification to any URL on rule match with optional secret header
- Bulk Apply — retroactively apply rules to existing mail in any folder via web UI or MCP
- Prometheus Metrics —
/metricsendpoint with counters, gauges (messages, rules, contacts, DB size, build info), and a tick-duration histogram for monitoring - Drag & Drop Rule Reorder — reorder rules via drag-and-drop on the rules page
- Rule Dry-Run — test rules against synthetic or real IMAP messages with per-condition pass/fail breakdown and actual vs expected values
- CardDAV Contacts Import — import contacts from iCloud address book
- Email Contact Collection — automatically extract contacts from processed messages
- Contact Autocomplete — contacts suggest in rule condition value inputs
- Rules Export/Import — backup and restore rule configurations as JSON
- Scheduled Rules Backup — email backups of rules as JSON attachments with configurable frequency (daily/weekly/monthly) and recipient
- Activity Log — see every rule match and action result with timestamps; select rows to delete individually or clear the whole log
- Stats Dashboard — rule hit counts, top senders, actions breakdown, daily/weekly volume (7/30/90-day range), messages by folder, poller health, and runtime metrics charts (Memory, CPU %). Stats persist independently from activity logs and export as CSV
- Keyboard Shortcuts —
?for help dialog,g+d/a/r/s/tfor navigation between pages - Docs Page — full usage guide and API reference with curl examples and syntax highlighting
- Themes — nine colour themes (Mailflow, Ayu, Catppuccin, Cyberpunk, Dracula, Gruvbox, Nord, One Dark, Tokyo Night), each in a dark and light variant; pick the theme in Settings → Regional and quick-toggle dark/light from the nav (respects OS preference on first visit; stored per browser)
- JetBrains Mono Font — optional monospace font, toggle in Settings → Regional
- Timezone Support — display activity log in any IANA timezone (searchable picker with validation)
- Folder Auto-Create — source folder is created on iCloud if it doesn't exist
- Test Connection — verify IMAP credentials before saving
- Configurable Polling — adjustable batch size, interval, and on/off toggle
- Log Retention — configure how many activity entries to keep
- MCP Server — remote access for AI agents (Claude Code, OpenCode, Codex) with 26 tools and API key auth
- Contacts Management — enable/disable automatic collection, import from CardDAV, wipe all contacts
- Server Metrics — uptime, memory, goroutines, and server time in Settings
- Health Endpoint — GET /health returns JSON (public, no auth required); unauthenticated callers get only
{"status": "ok"|"degraded"}, authenticated sessions get the full payload (version, uptime, DB/IMAP/poller state, stats)
- IMAP password encrypted at rest with AES-256-GCM (stored in the database, never written to
config.json) - CSRF protection on all forms
- Rate-limited login (5 attempts/minute/IP) and MCP access (100 requests/minute/IP)
- MCP API key authentication with constant-time comparison
- bcrypt hashed admin password
- SameSite=Strict session cookies
- Security headers on all responses — a strict nonce-based
Content-Security-Policy(nounsafe-inline/unsafe-evalfor scripts),X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy— and HSTS over HTTPS - Frontend libraries (HTMX, Lucide, Chart.js, highlight.js) are self-hosted under
/static; no scripts are loaded from third-party CDNs - Secure and HttpOnly cookies when behind TLS
- Logout requires POST with CSRF (no GET-based logout)
| Dashboard | Rules | Activity |
|---|---|---|
![]() |
![]() |
![]() |
| Settings | Stats | Docs |
![]() |
![]() |
![]() |
Incoming mail → iCloud Rule → "Processing" folder → Mailflow poller → Match rules → Execute actions
- Create an iCloud mail rule that moves all incoming mail to a "Processing" folder (see below)
- Mailflow polls the Processing folder every 300 seconds (5 minutes, configurable in Settings)
- Each message is checked against your rules (first match wins)
- Matched actions execute: move to folder, mark as read, etc.
- Unmatched messages fall through to the catch-all rule
Create a rule on iCloud to route mail to the Processing folder:
- Go to icloud.com/mail
- Click the settings gear ⚙ → Rules → Add a Rule
- Set "If a message" → is addressed to → leave the address field empty (matches all)
- Under "Then" → Move to Folder → choose New Folder... → enter Processing
- Click Done → Done
All new incoming mail will now land in the Processing folder.
go run ./cmd/mailflow/ -data=./dataOpen http://127.0.0.1:8080/setup — configure your admin password and iCloud app-specific password.
- Go to Rules → + New Rule
- Add a condition (e.g.
from contains @work.com) with ALL or ANY matching - Add an action (e.g.
Move to Folder → Work) - Save
The rule runs immediately on the next poll tick. Click Run Poll Now to trigger manually.
docker compose up -dThen open http://127.0.0.1:8080/setup to configure IMAP. After saving, restart the container so IMAP reconnects:
docker compose restartThe image is automatically built and pushed to ghcr.io/mojoaar/icloud-mailflow on every version tag.
Set LOG_LEVEL=debug in docker-compose.yml to enable verbose logging:
environment:
- TZ=Europe/Copenhagen
- LOG_LEVEL=debugUseful for troubleshooting — shows rule matching, condition evaluation, and poller state.
If you run Mailflow behind a reverse proxy, set TRUST_PROXY=true so rate limiting and logs use the forwarded client IP. Leave it unset when exposing Mailflow directly — otherwise X-Forwarded-For can be spoofed to bypass the login rate limit.
To build locally instead:
docker build -t icloud-mailflow .You can also deploy without cloning the repo — just point your docker-compose.yml at the pre-built image:
services:
mailflow:
image: ghcr.io/mojoaar/icloud-mailflow:latest
environment:
- TZ=Europe/Copenhagen
ports:
- "8080:8080"
volumes:
- ./data:/data
restart: unless-stoppedA demo database with sample data (rules, contacts, activity log) is available for testing and screenshots:
rm -f demo/mailflow.db* && bash scripts/demo.sh && go run ./cmd/mailflow/ -data=./demoThen open http://localhost:8080/dashboard and log in with password demo123.
To regenerate the README screenshots from the demo dataset (requires Node + Playwright — npm install && npx playwright install chromium):
bash scripts/screenshots.sh- Go 1.25.5+
- No CGO (pure Go SQLite via modernc.org/sqlite)
| Component | Technology |
|---|---|
| Language | Go 1.25+ |
| HTTP Router | chi v5 |
| Database | SQLite (modernc.org/sqlite) |
| IMAP | go-imap v2 |
| CardDAV | go-webdav/carddav |
| Metrics | Prometheus (prometheus/client_golang) |
| Frontend | HTMX + html/template, no JavaScript framework |
cmd/mailflow/ — entry point
internal/
config/ — JSON config file
crypto/ — AES encrypt/decrypt + bcrypt
db/ — SQLite + migrations + repositories
imap/ — IMAP client (go-imap v2)
rules/ — rule evaluation engine
contacts/ — contact collector from email headers
carddav/ — iCloud CardDAV contacts importer
mcp/ — MCP server for AI agent access (26 tools)
metrics/ — Prometheus metrics (counters, gauges, histogram)
poller/ — periodic email polling
smtp/ — SMTP MIME multipart email sender
web/ — chi router, auth, handlers, templates
Using the iCloud web mail client (mail.icloud.com) while Mailflow is polling may cause the web session to expire. This is Apple's session management, not a bug. Workarounds:
- Set a longer poll interval (300s+ recommended) in Settings
- Disable polling via the toggle in Settings while actively using iCloud web mail
- Use separate browsers for iCloud web and Mailflow
| Library | Use | License |
|---|---|---|
| go-imap v2 | IMAP client | MIT |
| mcp-go | MCP server framework | MIT |
| go-webdav | CardDAV client | MIT |
| go-vcard | vCard parsing | MIT |
| chi v5 | HTTP router | MIT |
| x/crypto | bcrypt password hashing | BSD-3-Clause |
| modernc.org/sqlite | SQLite driver (no CGO) | BSD-3-Clause |
| prometheus/client_golang | Prometheus metrics endpoint | Apache-2.0 |
| Asset | Use | License |
|---|---|---|
| Lucide Icons | SVG icons throughout the UI (self-hosted) | ISC |
| JetBrains Mono | Monospace font (optional, toggled in Settings) | OFL-1.1 |
| highlight.js | Syntax highlighting on the Docs page (self-hosted) | BSD-3-Clause |
| Chart.js | Charts on the Stats page (self-hosted) | MIT |
| HTMX | Frontend interactivity without JavaScript frameworks (self-hosted) | 0BSD |
MIT — see CHANGELOG.md for release history.





