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://bulwarkmail.org
  • TypeScript 98.5%
  • JavaScript 0.8%
  • CSS 0.4%
  • Shell 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-11 12:59:36 +01:00
.github fix: give the amd64 and arm64 images of one build the same build id, so mixed pods don't announce a new version #1531 2026-11-05 02:00:47 +01:00
.husky ci: lint and typecheck the whole tree in CI and only staged files in the pre-commit hook 2026-10-19 19:09:46 +02:00
app fix: keep the focus outline in Windows contrast themes when off is only the default 2026-11-10 23:44:16 +01:00
components fix: print a PDF from the file preview in the desktop app 2026-11-11 12:59:36 +01:00
contexts perf: stop re-rendering every message row and folder when a message opens 2026-10-15 03:18:13 +02:00
deploy chore: let the demo container join extra docker networks on deploy 2026-11-06 18:38:22 +01:00
desktop-policies feat: add a desktop setting and policy to always write messages in a new window 2026-11-11 01:51:31 +01:00
hooks fix: add a tag made in a group account's tag picker to the group's shared tags #1613 2026-11-10 19:33:17 +01:00
i18n feat: add a language picker and more accessibility options to the login page 2026-11-07 16:57:09 +01:00
integration fix: show and fold the folded column's names at once so the icons don't slide through the width transition 2026-11-06 19:35:00 +01:00
lib fix: print a PDF from the file preview in the desktop app 2026-11-11 12:59:36 +01:00
locales feat: add the desktop setting to open the app again after a restart 2026-11-11 02:12:06 +01:00
public chore: refresh the readme and install screenshots for 1.21.0 2026-11-07 22:04:55 +01:00
screenshots chore: refresh the readme and install screenshots for 1.21.0 2026-11-07 22:04:55 +01:00
scripts fix: let the Lite build pass with the impersonation return endpoint in the bundle #1538 2026-11-08 01:08:55 +01:00
stores feat: add a desktop setting and policy to always write messages in a new window 2026-11-11 01:51:31 +01:00
vendor/sqlite3mc/2.6.1-3.54.0 feat: add an encrypted local mail store that syncs in a worker 2026-11-03 04:57:20 +01:00
.dockerignore fix: compile the scripts the first screen runs while they download, in Chrome 2026-11-06 19:31:56 +01:00
.env.dev.example fix: lengthen example SESSION_SECRET 2026-08-20 14:44:19 +02:00
.env.example feat: keep an unticked sign-in until the browser closes when the admin allows it #1559 2026-11-07 23:34:42 +01:00
.gitattributes fix: keep the vendored database engine byte-exact on Windows checkouts 2026-11-03 04:57:58 +01:00
.gitignore feat: add an encrypted local mail store that syncs in a worker 2026-11-03 04:57:20 +01:00
ACCESSIBILITY.md docs: add an accessibility page for the repository overview 2026-10-31 22:36:01 +01:00
CHANGELOG.md chore: release 1.21.1 2026-11-11 00:46:04 +01:00
CONTRIBUTING.md docs: add contribution scope and issue-first rules to the contributing guide 2026-10-02 11:05:05 +02:00
docker-compose.yml fix: keep existing compose volumes and add the two that were missing 2026-10-31 18:12:00 +01:00
Dockerfile feat: run the Docker image as any uid in group 0 #1569 2026-11-07 16:19:23 +01:00
Dockerfile.lite ci: restore the Lite image build step as it was before the source map work 2026-11-01 06:24:38 +01:00
eslint.config.mjs chore: keep the vendored SQLite build out of lint 2026-11-03 19:49:57 +01:00
instrumentation.node.ts feat: let desktop users opt in to usage info and crash reports, each with its own switch 2026-11-03 15:55:17 +01:00
instrumentation.ts docs: point code comments at the monitoring guide where the website has it 2026-11-01 03:49:54 +01:00
LICENSE chore: allow app store distribution under a provisional AGPL permission 2026-09-30 12:11:01 +02:00
next.config.ts fix: compile the scripts the first screen runs while they download, in Chrome 2026-11-06 19:31:56 +01:00
NOTICE feat: add an airport time zone table for flight cards 2026-10-30 04:18:53 +01:00
package-lock.json chore: release 1.21.1 2026-11-11 00:46:04 +01:00
package.json chore: release 1.21.1 2026-11-11 00:46:04 +01:00
playwright.integration.config.ts chore: remove unused e2e smoke tests and leftover assets 2026-09-21 23:02:40 +02:00
postcss.config.mjs test: bring the main test suite back to green after the vitest 5 upgrade and header rework 2026-10-29 14:07:40 +01:00
proxy.ts fix: keep the signed-out redirect to top-level pages, out of caches and off other login paths 2026-11-07 09:16:16 +01:00
README.md docs: refresh the readme images and the web app manifest screenshots from the new demo 2026-10-18 20:38:44 +02:00
setup.sh fix: listen on the HOSTNAME and PORT from .env.local with npm start #1441 2026-10-30 00:01:29 +01:00
tsconfig.build.json fix: keep the production build's type check within 2 GB of memory #1431 2026-10-29 20:11:08 +01:00
tsconfig.json chore: keep gitignored local-data out of typecheck and lint 2026-09-26 12:34:06 +02:00
VERSION chore: release 1.21.1 2026-11-11 00:46:04 +01:00
vitest.config.ts test: check each page's first screen without scripts and its layout shift 2026-11-01 19:04:47 +01:00
vitest.setup.ts fix: keep the page skeleton as the server drew it instead of hydrating its thousands of shapes 2026-11-05 11:34:04 +01:00

Bulwark: webmail for Stalwart Mail Server. Mail, calendar, contacts and files in one browser client.

latest release  docker: ghcr.io/bulwarkmail/webmail  license: AGPL v3  Discord members

Website · Documentation · Live demo · Releases · Discord

Bulwark Webmail is a self-hosted webmail client for Stalwart Mail Server. It talks to Stalwart over JMAP and puts mail, calendar, contacts and files behind one login, with one set of settings and one admin dashboard.

Bulwark inbox on a laptop, with the same message open on a phone

Contents

Screenshots

Calendar week view with events from several calendars Contact details with recent mail and upcoming events
Calendar week view  ·  Contact details with recent mail and upcoming events

The composer with an @mention chip and the contact card it opens The inbox split diagonally between the light and dark theme
Composer with @mention cards  ·  Light and dark themes

Features

  • Mail: threading, unified inbox, cross-account views, full-text search, Sieve filters, S/MIME, templates, scheduled send
  • Calendar: month, week, day and agenda views, recurring events, iMIP invitations, CalDAV subscriptions
  • Contacts: several address books, groups, vCard import and export
  • Files: Stalwart's JMAP file storage, with previews, sharing and folder upload

All four share single sign-on and 2FA, multiple accounts, 27 languages, PWA install and web push, themes, plugins and keyboard shortcuts. The full list is on the All features page.

There are two editions. The full edition runs as a Node.js server and adds the admin dashboard, OAuth, plugins and settings sync. Bulwark Lite is the same client as static files, served by any web host or by Stalwart itself.

Quick start

docker run -d -p 3000:3000 ghcr.io/bulwarkmail/webmail:latest

Then open http://localhost:3000. A setup wizard asks for your Stalwart server and an admin password. The installation guide has the details, and Stalwart setup covers the mail server side.

Other ways to install

Method Guide
Docker Compose Compose
Behind a reverse proxy or on a sub-path Reverse proxy
From source, without Docker Manual install
Lite on any static host Static hosting
Lite as a container Container image
Lite served by Stalwart Install on Stalwart

Updating explains how to move to a new version.

Container images and release files from 1.12.0 on come with a signed build provenance attestation. To check that one was built by this repository's workflows, run gh attestation verify oci://ghcr.io/bulwarkmail/webmail:<version> --owner bulwarkmail for an image, or gh attestation verify <file> --repo bulwarkmail/webmail for a downloaded file. Release files also have a .sha256 next to them.

Configuration

Most installs are set up in the wizard on first launch and changed later in the admin dashboard. You can also use environment variables, which fit read-only or immutable deployments better. When both set the same key, the value saved in the admin config wins, so an environment variable only fills in what the admin config leaves unset.

JMAP_SERVER_URL=https://mail.example.com
APP_NAME=My Webmail
Topic Guide
Overview, config files and precedence Configuration
Every variable Environment reference
OAuth2 / OIDC and single sign-on Authentication, Embedded SSO
Several JMAP servers, custom endpoints Multi-server deployments, Custom endpoints
Branding, logos, per-domain branding Customization
Anonymous telemetry (off by default) Anonymous usage stats

Documentation

All documentation is at bulwarkmail.org/docs:

The pages are Markdown files in the website repository. Send corrections there.

Development

git clone https://github.com/bulwarkmail/webmail.git
cd webmail
npm install
cp .env.dev.example .env.local   # built-in mock JMAP server, no mail server needed
npm run dev
npm run typecheck
npm run lint
npx vitest run             # unit tests
npm run test:integration   # Stalwart in Docker + Playwright

The contributing guide covers tests, translations, code style and pull requests. Architecture explains how the code is organized.

The stack: Next.js 16 and React 19, TypeScript, Tailwind CSS v4, Zustand, Tiptap, next-intl, Tabler Icons, and our own JMAP client (RFC 8620). Tests run on Vitest and Playwright.

Use of AI

AI tools are part of how Bulwark is developed. A change is held to the same standard whether a person or a tool wrote it: it has to be understood, reviewed and pass the checks and tests before it is merged.

Bulwark itself has no AI features and doesn't send your mail, contacts, calendar or files to an AI service. If your Stalwart server classifies spam with an LLM, Bulwark shows the verdict, but the classification happens on your server.

Contributors can use AI tools too, as long as they use capable, current models and not small or outdated ones. The contributing guide explains what we expect.

Community and support

License

GNU AGPL v3 only, with an additional permission to distribute apps built from this code through app stores such as the Apple App Store and Google Play. The permission is provisional until every earlier contributor has agreed to it (consent request); contributions made since 30 September 2026 are already covered. This repository preserves the original MIT attribution for the fork lineage in NOTICE.

Acknowledgments

Thanks to root-fr/jmap-webmail and @ma2t for the groundwork this project builds upon.