OpenMail

**An agent-native, self-hosted mail server, written in Rust.** One binary that gives an AI agent its own real email address — receive, parse, thread, search and send actual SMTP mail on infrastructure you control — behind a clean REST API and an MCP server. Humans and agents are both first-class users. [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE) [![Status](https://img.shields.io/badge/status-v0.1%20WIP-orange)](./docs/adr/0004-milestones.md) > **Status: v0.1, work in progress, not yet released.** The workspace compiles > and the domain model is taking shape; it does not yet send or receive mail. > Follow [`docs/adr/0004-milestones.md`](./docs/adr/0004-milestones.md). --- ## Why this exists The hard part of an agent-mailbox product was never the API. It is the mail plumbing: receiving over SMTP/MX, sending with real deliverability (SPF/DKIM/DMARC, DANE/MTA-STS, IP reputation), parsing genuinely broken MIME, threading, and storage. Hosted agent-mail products solve this well and run on someone else's infrastructure, closed. The self-hostable mail servers that exist — Postfix+Dovecot, iRedMail, Stalwart — solve the plumbing but have no notion of an agent: no per-agent inbox provisioning, no threads as API resources, no MCP, no way for an agent to own a mailbox. **OpenMail is the intersection nobody occupies: agent-native, self-hostable, and permissively licensed.** ## What makes it agent-native - **Inboxes are API resources**, provisioned in one call — not Unix accounts. - **Threads are first-class**, stitched from `In-Reply-To`/`References`. An agent asks for a conversation, not a folder listing. - **`extracted_text`** — the reply with quoted history stripped. An agent that reads full bodies re-reads the whole thread every turn and burns its context window on text it already has. - **MCP server** — an agent owns and operates its own mailbox as tools. - **Webhooks + WebSocket** `message.received` events. Push, not poll. - **Humans too** — standard IMAP/SMTP access is a first-class goal, not an afterthought, so a person can point Apple Mail or Thunderbird at the same mailbox an agent is driving. ## Why Rust, and why our own crates Two implementations of the mail plumbing exist in a permissive licence: Mox (MIT, Go) and — for the primitives only — Stalwart's published crates (Apache-2.0/MIT, Rust). Stalwart's *server* is AGPL-3.0, which is why nobody has shipped a permissively licensed Rust mail server. We are building one. See [`docs/adr/0001-rust.md`](./docs/adr/0001-rust.md) for the decision and its honest costs. Concretely, this means writing what the Rust ecosystem does not have. At the time of writing, **`dane` and `mta-sts` do not exist on crates.io at all** — Stalwart keeps its implementations inside AGPL server crates. Ours ship standalone and permissive, so any Rust mail project can use them. ## The workspace Twelve crates in three tiers. Tier 1 is published to crates.io as a contribution to the Rust mail ecosystem and depends on nothing else here. ### Tier 1 — standalone, publishable | Crate | What | Prior art in Rust | |---|---|---| | [`mail-dane`](./crates/mail-dane) | DANE / TLSA verification for SMTP (RFC 7672) | **none — first permissive implementation** | | [`mail-mta-sts`](./crates/mail-mta-sts) | MTA-STS policy discovery, fetch, parse, cache (RFC 8461) | **none — first permissive implementation** | | [`mail-dsn`](./crates/mail-dsn) | Delivery Status Notifications (RFC 3464) | none | ### Tier 2 — the mail engine | Crate | What | |---|---| | [`openmail-smtpd`](./crates/openmail-smtpd) | Inbound SMTP: session state machine, STARTTLS, AUTH, PIPELINING | | [`openmail-relay`](./crates/openmail-relay) | Outbound: smarthost relays (SES, Oracle, generic) and direct-to-MX | | [`openmail-guard`](./crates/openmail-guard) | Abuse gate: iprev, DNSBL, rate limiting | | [`openmail-junk`](./crates/openmail-junk) | Per-inbox Bayesian spam classification | ### Tier 3 — the agent-native layer (the product) | Crate | What | |---|---| | [`openmail-core`](./crates/openmail-core) | Domain model, threading, quote-stripping. No I/O. | | [`openmail-store`](./crates/openmail-store) | Postgres metadata + S3-compatible blobs | | [`openmail-api`](./crates/openmail-api) | The v0 REST API | | [`openmail-mcp`](./crates/openmail-mcp) | MCP server | | [`openmail`](./crates/openmail) | The binary: `serve`, `smtpd`, `sender`, `mcp`, `migrate` | ### Third-party `mail-parser`, `mail-builder`, `mail-auth` (DKIM/DKIM2/SPF/DMARC/ARC), `smtp-proto` — all Apache-2.0 OR MIT, all from Stalwart Labs' separately published primitive crates — plus `hickory-resolver` for DNS and DNSSEC. **No AGPL, GPL, or LGPL code is linked into any OpenMail binary.** We use none of the Stalwart *server*. See [`NOTICE`](./NOTICE) and [`docs/adr/0003-own-crates.md`](./docs/adr/0003-own-crates.md). ## Sending: bring your own reputation, or build your own Outbound sits behind one interface with two paths: - **Relay** — SES, Oracle Cloud Email Delivery, SendGrid, Postmark, Resend, or any smarthost. Rents someone else's IP reputation; inbox placement on day one. Providers are declarative data, not special cases — [`crates/openmail-relay/src/providers.rs`](./crates/openmail-relay/src/providers.rs). - **Direct-to-MX** — we resolve MX and deliver ourselves, with MTA-STS and DANE enforced. Our reputation, our control, and a months-long IP warmup. Receiving is always ours. > ⚠️ **Oracle Cloud blocks outbound TCP/25** for tenancies created after > 2021-06-23. Inbound :25 is unaffected. So on OCI you *receive* directly and > *relay* outbound on 587 — direct-to-MX is not possible there at all. > [`docs/adr/0005-oracle-cloud.md`](./docs/adr/0005-oracle-cloud.md). ## Build ```bash cargo check --workspace # ~21s cold on a Ryzen 7 5800X cargo test --workspace cargo clippy --workspace --all-targets # zero warnings is the gate ``` `unsafe_code = "forbid"` across the workspace. This code parses hostile input from the open internet on port 25; there is no exception worth the risk. ## Licence **Apache-2.0.** Permissive on purpose: the point is that other people can build commercial products on top of this, including ones that compete with anything we might host later. See [`docs/adr/0002-apache-2.md`](./docs/adr/0002-apache-2.md) for why Apache-2.0 rather than MIT or AGPL.