Files
openmail/README.md
T
Karti TripathiandClaude Opus 5 36b15ddcaf Rewrite as a Rust, Apache-2.0 workspace
Supersedes the Go + embed-Mox design. The Go tree is removed; its
architecture doc is preserved at docs/archive/ARCHITECTURE-go-embed-mox.md
because its competitive analysis and data model still hold.

Five decisions recorded as ADRs:

  0001  Rust, not Go — accepting ~5,500 lines of protocol code that Mox
        would have given us free, to get the first permissively licensed
        Rust mail server. Costs stated plainly.
  0002  Apache-2.0, not MIT or AGPL — patent grant, trademark, CLA-free
        contribution. Public on GitHub; Gitea stays as the private fallback.
  0003  Stalwart's primitive crates (Apache-2.0/MIT) yes; its AGPL server
        crates never. DANE and MTA-STS sit on the AGPL side of that line,
        which is why we write our own.
  0004  Milestones, reordered: embedded inbound is required at launch.
  0005  Oracle Cloud blocks outbound :25, so direct-to-MX is impossible on
        the launch host. Split delivery is mandatory, not an on-ramp.

Twelve crates in three tiers. Tier 1 (mail-dane, mail-mta-sts, mail-dsn)
is standalone and publishable — no `dane` or `mta-sts` crate exists on
crates.io at all today.

openmail-relay ships the provider table as data, with SES and Oracle from
the start. Oracle's and Resend's SPF includes are deliberately None: a
guessed include turns the DNS check green against a mechanism the provider
does not honour, and mail still fails SPF silently.

cargo check/test/clippy/fmt all green; unsafe_code is forbidden workspace
wide; cargo-deny enforces the licence policy in CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:08:05 -07:00

141 lines
6.3 KiB
Markdown

<h1>OpenMail</h1>
**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.