The modern webmail your JMAP server deserves.
Point it at your Stalwart box — or any JMAP provider — and get a fast, local-first inbox, a real calendar, and an age-encrypted vault that keeps your password in native code, never in the browser. Runs in a tab or as a desktop app.
Built for people who live in their inbox.
Everything is fast because it runs against a local index first, then syncs in the background.
Instant, and offline
Mail renders from an on-device index the moment you open it. A quiet background sync keeps it current — no spinners, no reloads.
Passwords never reach JS
Credentials live in a Rust-side, age-encrypted vault. The web view never sees them, and the page makes no third-party requests.
Every account, one inbox
Add all your JMAP accounts and read them together in “All inboxes,” with per-account unread, search, and bulk triage.
Day, week, month
A real calendar built in: drag to move and resize, click to create, and secondary-timezone rails for scheduling across regions.
Hands on the keys
Navigate, triage, and schedule without the mouse. Actions apply optimistically and roll back only if the server disagrees.
Draft with your own model
An in-app assistant reads and drafts mail using your Claude subscription or any OpenAI-compatible endpoint — your key, your call.
Made for self-hosted Stalwart.
No account to create, no middleman. Type your email, webjmail finds your JMAP endpoint, and your mail stays between you and your server.
Enter your email
webjmail autodiscovers /.well-known/jmap from your domain — Stalwart, Fastmail, and standard JMAP servers just work.
Desktop app: nothing to configure
The native app talks to your server through a Rust layer, so there’s no CORS to deal with. Best choice for a self-hosted box.
Web app: allow the origin once
Browsers enforce CORS, so your server (or the proxy in front of it) needs to allow https://app.webjmail.com. One-time config, shown here →
# In front of Stalwart (JMAP on :8080) mail.example.com { @webjmail header Origin https://app.webjmail.com header @webjmail { Access-Control-Allow-Origin "https://app.webjmail.com" Access-Control-Allow-Credentials "true" Access-Control-Allow-Methods "GET, POST, OPTIONS" Access-Control-Allow-Headers "Authorization, Content-Type, Accept" } @preflight method OPTIONS respond @preflight 204 reverse_proxy 127.0.0.1:8080 }
# server { ... } fronting Stalwart location / { set $cors https://app.webjmail.com; if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin $cors always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; add_header Access-Control-Allow-Headers "Authorization, Content-Type, Accept" always; return 204; } add_header Access-Control-Allow-Origin $cors always; add_header Access-Control-Allow-Credentials "true" always; proxy_pass http://127.0.0.1:8080; }
With credentials you must echo the exact origin — * is rejected. Apply the
same headers to the /.well-known/jmap path so discovery isn’t blocked.
Run it in the browser, or install the desktop app.
The web app needs nothing but a link. The desktop build adds the encrypted vault and works fully offline.
$ paru -S webjmail
$ yay -S webjmail
Prefer a binary? Grab the AppImage, .deb, .rpm, or tarball from GitHub Releases.