https://github.com/bulwarkmail/webmail
JMAP Webmail built for the 21st Century. A modern, self-hosted email client for Stalwart Mail Server powered by the JMAP protocol. Email, calendar, contacts and files. Fast, private, and open source.
https://github.com/bulwarkmail/webmail
calendar contacts email email-client jmap mail nextjs self-hosted typescript webmail
Last synced: about 2 months ago
JSON representation
JMAP Webmail built for the 21st Century. A modern, self-hosted email client for Stalwart Mail Server powered by the JMAP protocol. Email, calendar, contacts and files. Fast, private, and open source.
- Host: GitHub
- URL: https://github.com/bulwarkmail/webmail
- Owner: bulwarkmail
- License: other
- Created: 2026-03-13T12:01:48.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-27T22:04:58.000Z (about 2 months ago)
- Last Synced: 2026-04-27T22:19:02.206Z (about 2 months ago)
- Topics: calendar, contacts, email, email-client, jmap, mail, nextjs, self-hosted, typescript, webmail
- Language: TypeScript
- Homepage: https://bulwarkmail.org
- Size: 10.5 MB
- Stars: 356
- Watchers: 6
- Forks: 45
- Open Issues: 21
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Notice: NOTICE
Awesome Lists containing this project
- stars - bulwarkmail/webmail - JMAP Webmail built for the 21st Century. A modern, self-hosted email client for Stalwart Mail Server powered by the JMAP protocol. Email, calendar, contacts and files. Fast, private, and open source. (TypeScript)
README

# Bulwark Webmail
A modern, self-hosted webmail client for [Stalwart Mail Server](https://stalw.art/), built with Next.js and the JMAP protocol.
[](LICENSE)
[](https://discord.gg/tYCujymGrT)
[](CHANGELOG.md)
[](https://ghcr.io/bulwarkmail/webmail)
---
## Screenshots


Calendar – month, week, day, and agenda views with drag-to-reschedule, iMIP invitations, and CalDAV subscriptions.
Contacts – multiple address books, groups, vCard import/export, and autocomplete in the composer.

Themes – bundled color themes or upload your own as ZIP bundles; admins can enforce presets.
Plugins – extend the client with bundled or third-party plugins installed from a .zip file.

Light mode – full theme support with intelligent color transformation for HTML emails.
Settings – appearance, identities, filters, templates, security, and more.
## Overview
Bulwark is a full webmail suite, not just an inbox. It bundles the four apps most self-hosters end up wanting on the same login:
- **Mail** – threading, unified inbox, full-text search, Sieve filters, S/MIME, templates
- **Calendar** – month/week/day/agenda, recurring events, iMIP invitations, CalDAV subscriptions
- **Contacts** – multiple address books, groups, vCard import/export
- **Files** – Stalwart's JMAP FileNode storage with previews and folder upload
Plus the infrastructure around them: OAuth2 / OIDC SSO, TOTP 2FA, multi-account (up to 5 at once), 15 languages, PWA install, dark/light themes, a plugin system with an extension marketplace, and a admin dashboard.
Full feature list: **[FEATURES.md](FEATURES.md)**.
---
## Quick Start
### Docker
```bash
docker run -d -p 3000:3000 \
-e JMAP_SERVER_URL=https://mail.example.com \
ghcr.io/bulwarkmail/webmail:latest
```
Or with Docker Compose:
```bash
cp .env.example .env.local
# Edit .env.local – set JMAP_SERVER_URL
docker compose up -d
```
### From Source
```bash
git clone https://github.com/bulwarkmail/webmail.git
cd webmail
npm install
cp .env.example .env.local
# Edit .env.local – set JMAP_SERVER_URL
npm run build && npm start
```
### Development
```bash
npm run dev # Dev server with a mock JMAP server
npm run typecheck
npm run lint
```
## Configuration
All variables are evaluated at runtime, so Docker deployments can be reconfigured without rebuilding. Edit `.env.local`:
```env
# Required
JMAP_SERVER_URL=https://mail.example.com
# Optional
APP_NAME=My Webmail
```
Server listen address
```env
HOSTNAME=0.0.0.0 # Default; use "::" for IPv6
PORT=3000
```
OAuth2 / OIDC
```env
OAUTH_ENABLED=true
OAUTH_CLIENT_ID=webmail
OAUTH_CLIENT_SECRET= # optional, for confidential clients
OAUTH_CLIENT_SECRET_FILE= # path to a file containing the secret
OAUTH_ISSUER_URL= # optional, for external IdPs
```
Endpoints are auto-discovered via `.well-known/oauth-authorization-server` or `.well-known/openid-configuration`.
Session & settings sync
```env
SESSION_SECRET= # openssl rand -base64 32
SESSION_SECRET_FILE=/session-secret # path to a file containing the secret
SETTINGS_SYNC_ENABLED=true
SETTINGS_DATA_DIR=./data/settings # mount as a volume in Docker
```
Credentials are encrypted with AES-256-GCM and stored in an httpOnly cookie (30-day expiry). Settings sync stores per-account preferences encrypted at rest and requires `SESSION_SECRET`.
Custom JMAP endpoint
```env
ALLOW_CUSTOM_JMAP_ENDPOINT=true
```
Shows a "JMAP Server" field on the login form. External servers must CORS-allow the webmail origin.
Branding & PWA
```env
APP_NAME=My Webmail
APP_SHORT_NAME=Webmail
APP_DESCRIPTION=Your personal mail
FAVICON_URL=/branding/favicon.svg
PWA_ICON_URL=/branding/icon.svg # falls back to FAVICON_URL
PWA_THEME_COLOR=#3b82f6
PWA_BACKGROUND_COLOR=#ffffff
APP_LOGO_LIGHT_URL=/branding/logo-light.svg
APP_LOGO_DARK_URL=/branding/logo-dark.svg
LOGIN_LOGO_LIGHT_URL=/branding/login-light.svg
LOGIN_LOGO_DARK_URL=/branding/login-dark.svg
LOGIN_COMPANY_NAME=My Company
LOGIN_WEBSITE_URL=https://example.com
LOGIN_IMPRINT_URL=https://example.com/imprint
LOGIN_PRIVACY_POLICY_URL=https://example.com/privacy
```
Extension directory
```env
EXTENSION_DIRECTORY_URL=https://extensions.bulwarkmail.org
```
Enables the admin marketplace for browsing and installing plugins and themes.
Stalwart integration & logging
```env
STALWART_FEATURES=true # password change, Sieve filters, etc.
LOG_FORMAT=text # "text" or "json"
LOG_LEVEL=info # error | warn | info | debug
```
Subpath / reverse proxy mount
To serve the webmail at a subpath (e.g. `https://example.com/webmail`):
```env
NEXT_PUBLIC_BASE_PATH=/webmail
NEXT_PUBLIC_LOCALE_PREFIX=always # avoids next-intl rewrite loops
```
Unlike most other variables, `NEXT_PUBLIC_BASE_PATH` is read at **build time** because Next.js bakes it into emitted asset URLs. To use it with the published Docker image, build your own image with the variable set:
```bash
docker build --build-arg NEXT_PUBLIC_BASE_PATH=/webmail -t bulwark-webmail .
```
Then point your reverse proxy at the container without stripping the prefix - the app expects to receive requests under `/webmail/...` and serves all routes (`/webmail/api/...`, `/webmail/_next/static/...`, `/webmail/sw.js`, etc.) accordingly.
## Keyboard Shortcuts
| Key | Action |
| ------------- | ----------------------- |
| `j` / `k` | Navigate between emails |
| `Enter` / `o` | Open email |
| `Esc` | Close / deselect |
| `c` | Compose |
| `r` / `R` | Reply / Reply all |
| `f` | Forward |
| `s` | Star |
| `e` | Archive |
| `#` | Delete |
| `/` | Search |
| `?` | Show all shortcuts |
## Tech Stack
| | |
| ------------- | ------------------------------------------------- |
| **Framework** | [Next.js 16](https://nextjs.org/) with App Router |
| **Language** | TypeScript |
| **Styling** | [Tailwind CSS v4](https://tailwindcss.com/) |
| **State** | [Zustand](https://zustand-demo.pmnd.rs/) |
| **Protocol** | Custom JMAP client (RFC 8620) |
| **i18n** | [next-intl](https://next-intl-docs.vercel.app/) |
| **Icons** | [Lucide React](https://lucide.dev/) |
## Why Stalwart?
[Stalwart](https://github.com/stalwartlabs/mail-server) is a Rust mail server with native JMAP support – not IMAP/SMTP with JMAP bolted on. It handles JMAP, IMAP, SMTP, and ManageSieve in a single self-hosted binary with no third-party dependencies.
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
## License
[GNU AGPL v3](LICENSE). This repository preserves the original MIT attribution for the fork lineage in [NOTICE](NOTICE).
## Acknowledgments
Thanks to [root-fr/jmap-webmail](https://github.com/root-fr/jmap-webmail/) and [@ma2t](https://github.com/ma2t) for the groundwork this project builds upon.