# OpenMail — goal and roadmap ## The goal **Be the mail server agents run on.** Self-hosted, Apache-2.0, agent-native — the option an enterprise can point MX at without trusting us, and a builder can build a commercial product on without asking us. Not a hosted product. Not open-core. See [`docs/adr/0006-self-hosted-first.md`](docs/adr/0006-self-hosted-first.md). ### How we'll know it worked | | Signal | |---|---| | **Technically** | An agent creates its own inbox, receives real mail from Gmail, and replies — with the reply landing in the inbox, not spam — with no human in the loop. | | **In the ecosystem** | `mail-dane` and `mail-mta-sts` are depended on by a Rust project that is not ours. | | **Commercially** | One business we did not talk into it is running OpenMail on their own infrastructure, on their own domain. | None of those are v0.1. They are what v0.1 is aimed at. --- ## Definition of ready: what "ready to build" means The scaffold is done. This is the checklist that says the *foundation* is finished and feature work can start without stopping to fix plumbing. - [x] Apache-2.0, `NOTICE`, licence policy enforced in CI (`cargo-deny`) - [x] 12-crate workspace; `check`/`test`/`clippy`/`fmt` green on x86_64 and aarch64 - [x] `unsafe_code = "forbid"` workspace-wide - [x] Postgres schema as an embedded migration - [x] Six decisions recorded as ADRs, each with its cost stated - [x] Multi-stage Dockerfile, non-root, no shell in the runtime image - [x] **OCI outbound port block measured, not assumed** — 25 blocked, 587/465/2525 open - [ ] Repo pushed to `karti-ai/openmail` - [ ] CI actually running (GitHub-hosted x86 + spark-1 aarch64 runner) - [ ] `openmail.karti.ai` live - [ ] `cargo publish --dry-run` clean on the three tier-1 crates --- ## v0.1 — the agent layer, provable without mail **Thesis to prove:** the agent-native model is worth having, independent of whether we can move mail yet. Everything here is testable in CI with no mail infrastructure at all. - [ ] `openmail-core::extract::strip_quoted` — the first real algorithm. Prefer under-stripping to over-stripping; losing new content is unrecoverable, keeping a few quoted lines only costs tokens. - [ ] `openmail-core::thread` — both bases (`Headers`, `SubjectHeuristic`) with the time window that bounds the wrong answer - [ ] `openmail-store` — inbox/message/thread queries, S3 blob client - [ ] `openmail-api` — the v0 handlers behind bearer auth - [ ] **`POST /v0/inboxes/{id}/ingest`** — the loop-closer. Feed it a raw `.eml`, get back a threaded, extracted message. This is the milestone. - [ ] A corpus of ugly real-world `.eml` fixtures. Not synthetic — Outlook, Gmail, mobile clients, and at least one message that is legally MIME and practically deranged. **Done when:** `docker compose up`, POST a real message, get correct threading and clean `extracted_text` back. ## v0.2 — receive - [ ] `openmail-smtpd` on :25 with `Limits` enforced and a timeout on every state - [ ] `openmail-guard` — iprev, DNSBL, rate limit, in that order (never spend a DNS round trip on a connection a counter can reject) - [ ] `mail-auth` SPF/DKIM/DMARC verdicts recorded per message - [ ] `openmail-junk` per-inbox scoring - [ ] `message.received` webhooks + WebSocket - [ ] Real MX for a test domain; open OCI inbound :25 and prove mail arrives ## v0.3 — send, and the surface agents actually use - [ ] Relay send via **both** SES and Oracle from the start — two providers is what forces the abstraction to stay data-driven - [ ] Local DKIM signing - [ ] `mail-dsn` bounce handling wired to `outbox` state - [ ] `openmail-mcp` served — an agent creates an inbox, receives, and replies, with no human involved ## v0.4 — direct-to-MX, and the crates ship - [ ] `mail-dane` complete — DNSSEC-validated TLSA, and the property that an unauthenticated set can never return `Match` - [ ] `mail-mta-sts` complete — policy cache where a fetch failure never evicts a valid policy - [ ] **Publish both to crates.io.** First permissive implementations in Rust. - [ ] Direct MX delivery with both enforced. Not on OCI — that host blocks :25. - [ ] IP warmup, FBL enrolment, suppression lists ## v1.0 - [ ] IMAP front-end so a human uses their own client against the same mailbox - [ ] Multi-tenancy beyond `pods` - [ ] A deliverability track record worth publishing --- ## Standing rules 1. **Never copy from an AGPL/GPL tree.** Stalwart's server, maddy, BillionMail are read-only reference. Work from RFCs. ADR 0003. 2. **Silent failures are the highest-severity class.** A DANE downgrade or a mis-cached MTA-STS policy does not error — it quietly removes a security property or defers mail nobody sees. Every outcome in those crates is an explicit enum with no `Default` and no `bool`. 3. **Measure, don't assume.** The OCI port block was a documented "fact" that community reports contradicted; ten minutes of testing settled it. Do that every time. 4. **`pods` is not dead code.** It is the only thing keeping a future hosted offering migration-free. 5. **Zero clippy warnings is the gate**, not an aspiration.