mailx is an experimental, single-tenant, multi-account jmap service for Cloudflare Workers, Email Workers, D1, KV, and R2/S3-compatible object storage.
This project is not a complete JMAP server. It implements only a pragmatic minimum subset of two RFCs:
- RFC 8620, JMAP Core
- RFC 8621, JMAP Mail
The implementation is aimed at lightweight personal or small-team deployments and real-world webmail compatibility experiments. Expect protocol gaps, client-specific compatibility fixes, and behavior that intentionally degrades to simpler polling or storage models.
This is an experimental project. Do not treat it as a production-grade mail server, compliance archive, enterprise mailbox, or full IMAP/JMAP replacement.
The current goal is to provide enough JMAP Core/Mail behavior for basic webmail workflows:
- Sign in
- List mailboxes
- Receive mail through Cloudflare Email Routing
- Read/search simple message lists
- Save drafts
- Send mail through Resend
- Upload/download blobs and attachments
- Track basic mail state changes
| Feature | Status | Notes |
|---|---|---|
| Receiving mail | Fully usable | Cloudflare Email Routing delivers to the Email Worker; raw MIME is stored in object storage and metadata is written to D1. |
| All mail / mailbox listing | Fully usable | Mailbox/get, Mailbox/query, Email/query, and mailbox counters cover the basic mailbox/all-mail views used by tested clients. |
| Marking messages | Fully usable | Supports common keyword changes such as $seen, $flagged, and $draft. |
| Moving between folders | Fully usable | Supports mailboxIds patches for archive, inbox removal, and normal folder moves. |
| Drafts | Fully usable | Supports Email/set create for draft creation, body values, recipients, keywords, and mailbox assignment. |
| Sending mail | Partially usable | Resend delivery succeeds, but at least one tested client still reports failure in the UI despite successful delivery/webhook callbacks. This remains a JMAP response/client-compatibility issue. |
| Attachments | Partially usable | Basic inbound/outbound attachment upload, send, parse, and download paths exist. MIME tree fidelity, inline CID handling, and complex multipart structures are still simplified. |
| Templates | Partially usable | Template-like drafts can be saved and reused manually, but there is no first-class template model or template API. |
| Search | Partially usable | Basic mailbox/keyword query behavior exists; full-text search and full RFC filter/sort semantics are not implemented. |
| Threads | Partially usable | Lightweight thread ids are exposed, but conversation grouping is minimal. |
| Submission status | Partially usable | Resend webhook updates submission state, but event history and deduplication are incomplete. |
| Push/realtime sync | Degraded | EventSource is only a compatibility endpoint; clients should fall back to polling Foo/changes. |
| Feature | Status | Notes |
|---|---|---|
| Worker HTTP app | Fully usable | Hono app with JMAP and /v0/* APIs. |
| Email Worker entrypoint | Fully usable | Handles inbound mail from Cloudflare Email Routing. |
| Local auth | Fully usable | Username/password login with Bearer tokens. |
| JMAP Basic auth | Fully usable | Supports username or assigned email identity. |
| Account management | Fully usable | Single-tenant multi-account management through /v0/management. |
| Identity management | Fully usable | Admin-assigned sending identities and user-editable identity metadata. |
| Dynamic domains | Fully usable | DOMAIN supports JSON array strings and dynamic postmaster/catch-all behavior. |
| Resend relay | Partially usable | Sends through Resend HTTP API; no durable retry queue. |
| Resend webhooks | Partially usable | Delivery callbacks update current submission state; no complete svix-id dedupe/event history. |
| Object storage | Partially usable | R2/S3 abstraction stores raw messages and uploads; no historical object migration or full GC. |
| Local runtime | Fully usable | SQLite, file KV, and file object storage for tests/local development. |
| Feature | Status | Notes |
|---|---|---|
| Full RFC 8620 compliance | Not implemented | Only a minimal practical subset is implemented. |
| Full RFC 8621 compliance | Not implemented | Only common mail workflows are implemented. |
| IMAP/SMTP/POP3/LMTP/ManageSieve | Not implemented | mailx is not a traditional mail server stack. |
| Sieve filtering | Not implemented | No server-side filtering language. |
| JMAP WebSocket, RFC 8887 | Not implemented | Clients must use polling. |
| JMAP Quotas, RFC 9425 | Not implemented | No quota capability or enforcement. |
| Calendar/contacts/tasks | Not implemented | Mail only. |
| Multi-tenant deployment | Not implemented | One deployment is one tenant. |
| End-user self-registration | Not implemented | Accounts are admin/management-key created. |
| OAuth/OIDC login | Not implemented | Placeholder documentation only. |
| Full-text search | Not implemented | No search index. |
| Advanced JMAP filter/sort/collation | Not implemented | Basic filters only. |
| Complete MIME editing | Not implemented | No robust inline CID rewriting, calendar invite handling, signing, or encryption support. |
| DKIM/SPF/DMARC implementation | Not implemented | Delegated to external DNS/relay/provider behavior. |
| Spam/virus scanning | Not implemented | No content safety pipeline. |
| Mailbox ACL/shared mailboxes | Not implemented | No shared mailbox model. |
| Quota enforcement | Not implemented | No storage/send quota enforcement. |
| Durable outbound retry queue | Not implemented | Failed Resend calls are recorded as failed submissions. |
Several behaviors intentionally degrade to simpler implementations:
- Push degrades to polling.
/jmap/eventsourceexists for compatibility, but it does not provide continuous real-time push. - WebSocket is not advertised or implemented.
- Query changes degrade to conservative remove/add replacement for updated messages.
- Mailbox counters are recomputed from D1 relationships instead of maintained as authoritative counters.
- Attachments are downloaded by reparsing the stored raw message and extracting the requested attachment part.
- Threading degrades to simple stored or virtual thread ids rather than complete RFC-grade conversation grouping.
- Outbox degrades to client compatibility cleanup rather than a durable server-side send queue.
- Delivery tracking degrades to Resend state snapshots in
email_submissions.relay_responserather than a full event history. - Session discovery without credentials returns public discovery with empty accounts so clients can discover the endpoint before auth.
- Unknown or unsupported JMAP methods return
unknownMethodrather than attempting broad compatibility shims.
Required Worker configuration:
TOKEN_SECRETMANAGEMENT_KEYPUBLIC_BASE_URL- D1 binding:
DB - KV binding:
KV - R2 binding:
BUCKET, or compatible object storage configuration
Common optional configuration:
DOMAIN, JSON array string such as["example.com"]RESEND_API_KEYRESEND_WEBHOOK_SECRETRESEND_FROM_DOMAINTOKEN_TTL_SECONDSUPLOAD_TTL_SECONDSSEND_RATE_LIMITSEND_RATE_WINDOW_SECONDS
TOKEN_SECRET, MANAGEMENT_KEY, and PUBLIC_BASE_URL are reserved deployment configuration and cannot be overwritten through the management API.
Other mutable config may be stored with /v0/management Config/set, including secret values such as RESEND_API_KEY and RESEND_WEBHOOK_SECRET.
New inbound, imported, and draft messages are stored as raw RFC822/MIME objects:
emails/{accountId}/{emailId}.eml
For new messages:
blobId = emailIdstorage_key = emails/{accountId}/{emailId}.eml
Uploaded temporary blobs are stored separately under account upload paths and may be referenced by draft creation or import flows.
Webhook endpoint:
https://<your-public-base-url>/webhooks/resend
Recommended Resend events:
email.sentemail.deliveredemail.delivery_delayedemail.bouncedemail.failedemail.complainedemail.suppressed
Optional tracking events:
email.openedemail.clicked
Do not enable unrelated domain/contact events unless you add handlers for them.
Common checks:
bun run format
bun run typecheck
bun run test
bun run test:localCloudflare Workers pool tests may fail in constrained containers due to workerd/tcmalloc virtual address space assumptions. The local runtime tests are the primary fast feedback path for this repository.
Additional API and design notes live in:
docs/spec.mddocs/v0-api.md
This project is released under CC0 1.0 Universal. See LICENSE.