Architecture v3: pluggable MailBackend; competitive positioning; relay-first
- Confirm stack: Go, single binary, embed Mox + pluggable backend (chosen after landscape research). - Add MailBackend interface: relay (v1 default) / imap_smtp (BYO mailbox) / embedded (go-smtp + mox smtpclient/dane/mtasts, flagship). Deliverability becomes opt-in; useful on day 1. - Add competitive positioning (§0.1): closest competitor agenticmail is TS + Stalwart(AGPL) Docker sidecar; OpenMail differentiates as single binary, all-MIT, in-process. agentic-inbox is Cloudflare-locked. - Reorder milestones backend-first: relay loop before embedded SMTP. - State scope: self-hostable app, not a SaaS — no billing/Stripe. - README: pluggable backends + positioning. Repo layout: internal/mail/*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+66
-11
@@ -3,8 +3,13 @@
|
||||
Agent-native, self-hosted mail server. **One Go binary** that embeds [Mox](https://github.com/mjl-/mox)'s
|
||||
(MIT) mail internals for the correctness-critical 95% — DKIM, SPF/DMARC, DANE + MTA-STS delivery,
|
||||
real-world MIME, spam filtering — and owns a **native, agent-shaped data model** (Postgres + object
|
||||
store) with a REST API and MCP server on top. This document is the design of record. It favors an
|
||||
MVP that actually receives and sends real mail over a perfect feature set.
|
||||
store) with a REST API and MCP server on top. Mail I/O sits behind a pluggable **`MailBackend`**
|
||||
interface so OpenMail is useful on day one against a relay or an existing mailbox, while embedded-Mox
|
||||
self-host is the flagship, in-process path. This document is the design of record. It favors an MVP
|
||||
that actually receives and sends real mail over a perfect feature set.
|
||||
|
||||
> **Scope:** OpenMail is a **self-hostable app, not a SaaS** — no billing/payments, no Stripe, no
|
||||
> hosted control plane. Everything runs on the operator's hardware.
|
||||
|
||||
## 0. The decision: embed Mox, don't rebuild and don't just wrap it
|
||||
|
||||
@@ -45,6 +50,49 @@ clean. We embed the latter and replace the former with our own Postgres-backed s
|
||||
> they assume Mox's storage/config. Importing them is the line between "embed as a library" and
|
||||
> "fork Mox." We stay on the library side.
|
||||
|
||||
## 0.1 Competitive positioning (why OpenMail exists)
|
||||
|
||||
The agent-mailbox concept is already taken; the *implementation niche* is open.
|
||||
|
||||
| Project | Lang | License | How mail runs | Gap OpenMail fills |
|
||||
|---|---|---|---|---|
|
||||
| **agenticmail** (closest competitor) | TypeScript | MIT | TS orchestrator + **Stalwart in a Docker sidecar**, SQLite | Multi-container, AGPL mail engine, SQLite — vs. **single Go binary, all-MIT, in-process, Postgres** |
|
||||
| cloudflare/agentic-inbox | TS | Apache-2 | **Locked to Cloudflare Workers** + Email Routing | Vendor-neutral, runs anywhere |
|
||||
| Mox / Maddy / Stalwart | Go/Rust | MIT/GPL/AGPL | Full mail servers | Not agent-native (no agent API/MCP, no threads/extract) |
|
||||
|
||||
OpenMail's three differentiators, all downstream of the Go + embed-Mox choice:
|
||||
1. **Single static binary** (vs. sidecar/multi-container).
|
||||
2. **Fully MIT stack** — Mox is MIT, vs. agenticmail's AGPL Stalwart — so others can build commercial agents on top.
|
||||
3. **In-process mail engine** — nobody else does this; it's only possible because Mox is importable Go.
|
||||
|
||||
## 0.2 The `MailBackend` interface (pluggable mail I/O)
|
||||
|
||||
All inbound delivery and outbound sending sit behind one interface, so the agent layer (API, MCP,
|
||||
store, threading) never depends on *how* mail moves. Three backends, shipped in order of effort:
|
||||
|
||||
```go
|
||||
// MailBackend abstracts where mail comes from and how it leaves. The core never
|
||||
// knows which implementation is active.
|
||||
type MailBackend interface {
|
||||
// Send dispatches an already-built, DKIM-signable RFC 5322 message.
|
||||
Send(ctx context.Context, msg *OutgoingMessage) (SendResult, error)
|
||||
// Start begins delivering inbound messages to the sink until ctx is cancelled.
|
||||
// (relay/imap: poll or webhook; embedded: go-smtp on :25.)
|
||||
Start(ctx context.Context, sink InboundSink) error
|
||||
Capabilities() Caps // self-host? inbound-push? custom-domain? throwaway-addrs?
|
||||
}
|
||||
```
|
||||
|
||||
| Backend | Inbound | Outbound | Ops burden | Ships |
|
||||
|---|---|---|---|---|
|
||||
| **relay** | provider webhook / poll | SES / Postmark / Resend API (mox `dkim` sign) | lowest | **v1 default** |
|
||||
| **imap_smtp** | IMAP IDLE on a BYO mailbox | SMTP submission to BYO server | low | v1 |
|
||||
| **embedded** (flagship) | `go-smtp` :25 + mox verify/parse/junk | mox `smtpclient` + `dane` + `mtasts` (MX) | highest | milestone 5 |
|
||||
|
||||
This makes the deliverability slog *opt-in*: a user gets a working agent mailbox immediately via
|
||||
relay or their existing mailbox, and graduates to fully self-hosted SMTP only when they want to own
|
||||
the whole stack. The `embedded` backend is the differentiator; the other two are the on-ramp.
|
||||
|
||||
## 1. Component overview
|
||||
|
||||
```
|
||||
@@ -173,22 +221,29 @@ A thin MCP front-end over `core`: tools `create_inbox`, `list_messages`, `get_th
|
||||
|
||||
## 6. MVP milestones
|
||||
|
||||
Backend-first ordering: get an end-to-end loop on the *lowest-ops* backend, then add the differentiator.
|
||||
|
||||
1. **Core API + storage** — inboxes/messages/threads/drafts CRUD on Postgres + MinIO; bearer auth;
|
||||
ingest endpoint to seed without real mail. *Proves the data model + API.*
|
||||
2. **Inbound** — `go-smtp` on :25 + mox verify/parse/junk + threading + `message.received` webhook.
|
||||
MX a test domain at the VPS. *Proves receive.*
|
||||
3. **Outbound via relay** — `send`/`reply`, mox-DKIM-signed, through a relay. *Proves the loop: an
|
||||
agent receives and replies.*
|
||||
4. **MCP server** — agent owns and operates an inbox end-to-end.
|
||||
5. **Self-host SMTP send** — wire mox `smtpclient`/`dane`/`mtasts`; the deliverability/warmup long tail.
|
||||
ingest endpoint to seed without real mail. *Proves the data model + API.* `MailBackend` interface
|
||||
defined; a `null`/ingest backend satisfies it.
|
||||
2. **Relay backend (loop closed)** — `imap_smtp` and/or `relay` backend: inbound via IMAP IDLE or
|
||||
provider webhook → mox `message` parse + threading + `message.received`; outbound via SMTP
|
||||
submission / relay API, mox-DKIM-signed. *Proves receive+reply against real mail with near-zero ops.*
|
||||
3. **MCP server** — agent owns and operates an inbox end-to-end (tools over the core).
|
||||
4. **Embedded inbound** — `go-smtp` on :25 + mox verify/parse/junk + anti-abuse; MX a test domain.
|
||||
*Proves OpenMail can receive directly.*
|
||||
5. **Embedded outbound + deliverability** — mox `smtpclient`/`dane`/`mtasts`; the flagship self-host
|
||||
path and the IP-warmup/reputation long tail.
|
||||
|
||||
## 7. Repo layout (planned)
|
||||
|
||||
```
|
||||
cmd/openmail/ # single binary: `serve`, `smtpd`, `sender`, `mcp` subcommands
|
||||
internal/core/ # inbox/message/thread/draft services
|
||||
internal/smtp/ # go-smtp inbound server; calls mox verify/parse/junk + threading
|
||||
internal/sender/ # Sender interface: smtp (mox smtpclient+dane+mtasts) & relay backends; mox dkim
|
||||
internal/mail/ # MailBackend interface + shared parse/thread/DKIM helpers (mox pkgs)
|
||||
internal/mail/relay/ # relay backend (SES/Postmark/Resend)
|
||||
internal/mail/imapsmtp/ # imap_smtp backend (BYO mailbox: IMAP IDLE in, SMTP submit out)
|
||||
internal/mail/embedded/ # embedded backend (go-smtp :25 in; mox smtpclient+dane+mtasts out)
|
||||
internal/store/ # pgx/sqlc queries, object-store client, embedded migrations
|
||||
internal/store/migrations/ # SQL migrations (go:embed'd into the binary)
|
||||
internal/api/ # chi HTTP handlers (v0 surface)
|
||||
|
||||
@@ -9,6 +9,12 @@ internals of [Mox](https://github.com/mjl-/mox) (also MIT) for the hard, correct
|
||||
plumbing — DKIM, SPF/DMARC, DANE + MTA-STS secure delivery, real-world MIME parsing, spam
|
||||
filtering — and layers a native, agent-shaped data model (Postgres + object storage) and API on top.
|
||||
|
||||
Mail I/O is **pluggable**: start in minutes against a relay (SES/Postmark/Resend) or your existing
|
||||
mailbox (IMAP/SMTP), and graduate to a fully self-hosted, in-process SMTP engine when you want to own
|
||||
the whole stack. The in-process engine is the part nobody else ships — the only comparable project,
|
||||
[agenticmail](https://github.com/agenticmail/agenticmail), runs a Stalwart (AGPL) mail server in a
|
||||
Docker sidecar; OpenMail is **one static binary, fully MIT, no sidecar.**
|
||||
|
||||
> **Status: early WIP, private during initial build.** Will be released MIT-licensed and public.
|
||||
> Designed only from public RFCs and public API surfaces — nothing proprietary.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user