cloud-1 (phx) and cloud-2 (us-sanjose-1) are both already OCI aarch64 instances, so the open question in ADR 0005 was answerable directly rather than by reading forum posts. Measured from cloud-2: BLOCKED 25 gmail-smtp-in.l.google.com, aspmx.l.google.com OPEN 587/465/2525 gmail, sendgrid, SES, OCI Email Delivery The community reports claiming 587 and 2525 are also blocked are wrong. Only 25 is blocked, exactly as Oracle documents, so relay works and the launch host is viable. Inbound :25 reachability is still unverified — that is a VCN security-list change, not a platform limit. ROADMAP.md carries the goal, three success signals that are deliberately not v0.1, a definition-of-ready checklist, and five standing rules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
5.1 KiB
5.1 KiB
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.
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.
- Apache-2.0,
NOTICE, licence policy enforced in CI (cargo-deny) - 12-crate workspace;
check/test/clippy/fmtgreen on x86_64 and aarch64 unsafe_code = "forbid"workspace-wide- Postgres schema as an embedded migration
- Six decisions recorded as ADRs, each with its cost stated
- Multi-stage Dockerfile, non-root, no shell in the runtime image
- 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.ailivecargo publish --dry-runclean 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 answeropenmail-store— inbox/message/thread queries, S3 blob clientopenmail-api— the v0 handlers behind bearer authPOST /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
.emlfixtures. 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-smtpdon :25 withLimitsenforced and a timeout on every stateopenmail-guard— iprev, DNSBL, rate limit, in that order (never spend a DNS round trip on a connection a counter can reject)mail-authSPF/DKIM/DMARC verdicts recorded per messageopenmail-junkper-inbox scoringmessage.receivedwebhooks + 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-dsnbounce handling wired tooutboxstateopenmail-mcpserved — an agent creates an inbox, receives, and replies, with no human involved
v0.4 — direct-to-MX, and the crates ship
mail-danecomplete — DNSSEC-validated TLSA, and the property that an unauthenticated set can never returnMatchmail-mta-stscomplete — 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
- Never copy from an AGPL/GPL tree. Stalwart's server, maddy, BillionMail are read-only reference. Work from RFCs. ADR 0003.
- 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
Defaultand nobool. - 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.
podsis not dead code. It is the only thing keeping a future hosted offering migration-free.- Zero clippy warnings is the gate, not an aspiration.