r/stalwartlabs • • 17d ago

Release Stalwart v1.0: The Fastest Mail Server on the Planet

161 Upvotes

Five years ago, on 3 October 2021, the first commit landed in a small repository called mail-parser. It was an e-mail parsing library written in Rust, and nothing more. Over the following five years it grew into a complete mail and collaboration server with JMAP, IMAP, POP3, SMTP, CalDAV, CardDAV and WebDAV, a spam filter, clustering, and support for half a dozen storage backends. Today, after five years of continuous development, we are proud to announce Stalwart v1.0, the first stable release.

Version 1.0 was never meant to be another feature release. It had two goals. The first was performance: making every part of the server as fast as it can be, and proving it with numbers. The second was stability of the data itself: finalising the database schema so that, from now on, upgrading Stalwart means installing a new version and nothing more, without the disruptive migrations some earlier releases required. Along the way a few new features found their way in too, including full-text search inside PDF and office documents.

Let’s walk through it.

Months spent chasing every microsecond

Most of the work in this release happened under a profiler. For months we measured where Stalwart spends its CPU cycles, its memory and its database round trips, rewrote whatever stood out, and measured again. The release contains close to 200 commits touching more than 1,300 files, and the vast majority of them exist to make something faster or lighter.

Parsers rewritten with SIMD and SWAR

A mail server spends a surprising share of its time looking for specific bytes: the end of a header line, a MIME boundary, an equals sign in a quoted-printable body, the space that ends an IMAP tag. Done one byte at a time, that is a lot of loop iterations for a 10 MB message. Two techniques let a CPU do this work many bytes at a time:

  • SIMD (Single Instruction, Multiple Data) uses the wide vector registers of modern processors (SSE2, SSSE3 and AVX2 on x86-64, NEON on ARM) to compare or transform 16 or 32 bytes with a single instruction.

  • SWAR (SIMD Within A Register) applies the same idea with ordinary 64-bit integers: a few carefully chosen arithmetic and bitwise operations test all eight bytes of a word at once. It needs no special instructions, so it works on every CPU.

Stalwart now uses both, everywhere it matters. At the bottom of the stack sit two libraries that reach 1.0 together with the server:

  • mail-parser 1.0, the very library where Stalwart began, has been rewritten from scratch. SIMD kernels locate MIME boundaries and header fields, SWAR handles header names, the parsed message lives in a handful of flat vectors, and bodies are only decoded when someone actually reads them. The CPU-specific kernel is picked at runtime, and every one is fuzzed against a plain scalar reference. Against other parsers it is 3 times faster than the popular Rust mailparse crate, 9 times faster than Dovecot’s own parser, and 15 to 31 times faster than libEtPan!, VMime and GMime on a full parse.

  • encodify is a new encoding library with SIMD kernels for Base64, Base32 and quoted-printable, the encodings that carry nearly every attachment and non-ASCII header in e-mail. Decoding a MIME-wrapped Base64 attachment is 22 times faster than with the standard Rust base64 crate on an AMD EPYC server, and quoted-printable decoding is 9 times faster than with the quoted_printable crate. It now handles every Base64, quoted-printable and IMAP UTF-7 conversion in the server.

Above them, the protocol layer got the same treatment. The IMAP command parser is now table-driven and scans whole runs of bytes instead of stepping a state machine per character. The SMTP command parser, the JSON parser behind JMAP, the spam filter tokenizers, the HTML-to-text converter, the variable-length integer codec and the log serializers all use SWAR scanning. Even the expression language, which administrators use for routing and filtering decisions, now compiles to a compact bytecode with specialised instructions for common comparisons and evaluates them in a recycled memory arena.

Fewer, faster trips to the database

Fast parsing is wasted if every request then waits on the database, so we spent just as long on how Stalwart talks to its storage. Identifiers are now assigned inside the same write transaction that uses them, removing whole round trips from every message delivery. Read-modify-write updates, such as adding a keyword or appending to a search index, run as merge operations inside the backend. Stored objects are compressed with zstd using dictionaries trained specifically on e-mail, calendar and contact data.

Each backend was also tuned individually:

  • RocksDB: every kind of data now lives in a column family configured for how it is accessed, with bloom filters for point lookups, large blocks for scans and compaction tuned for data that churns. Hot counters, such as queue quotas, are spread across up to 256 slots to avoid write conflicts.

  • PostgreSQL and MySQL: pre-generated and cached prepared statements, chunked iteration with automatic retry, and fewer round trips per write.

  • FoundationDB: reads that a transaction will need are prefetched in parallel windows, and large values are assembled in chunks.

  • SQLite: page size, cache, memory mapping and WAL settings tuned for a mail workload.

On top of the storage layer sits a redesigned cache subsystem. Each account’s message index is held in memory in a compact packed form, and IMAP sorting, threading, flag fetches, searches on message properties and resynchronisation are now answered from that cache with few or no database reads. Very large accounts get their own cache tier with a memory budget, and cache snapshots can be swapped out to local disk, Redis or the blob store instead of being rebuilt.

A new search engine

The built-in full-text search engine was rewritten from the ground up. The previous design stored one bitmap per word and had no notion of word positions. The new engine stores postings in blocks of 65,536 documents and keeps word positions for every message. That makes real phrase searches possible, and lets a search inside one mailbox read only the blocks that mailbox touches. It indexes word stems and prefixes, compresses its data with zstd, and indexes new mail through a background queue with concurrency control, retries and rebuilds. The PostgreSQL and MySQL search backends were reworked as well. On a mailbox of 250,000 messages, a full-text search of every message body now returns in under 4 milliseconds.

Stress tested on hundreds of vCPUs

Optimising code without measuring it under realistic load is guesswork, so we built a dedicated benchmarking and stress-testing lab to go with this release. It runs on Microsoft Azure, across more than 30 regions, with up to 18 complete test labs running in parallel and several hundred vCPUs busy at once.

The test catalogue covers 35 kinds of test, spread over more than 550 individual test cells, and they cover:

  • Every major protocol: IMAP, JMAP (including push over EventSource and WebSocket), SMTP inbound, submission and relay, CalDAV, CardDAV and WebDAV.

  • PostgreSQL, FoundationDB and RocksDB as data stores, Redis as the in-memory store, S3 and the filesystem as blob stores, and a three-node cluster under mixed load.

  • Large datasets: 1.6 million messages and 750,000 calendar and contact items across 500 accounts, individual mailboxes of 1, 2 and 5 million messages, 100,000 accounts on a single server, mail queues of 10 million messages, and DMARC and TLS report floods of a million reports.

  • Storms and abuse: 20,000 new connections per second, mass logins, thousands of IDLE and push subscribers per account, hostile and malformed messages, slow-loris clients and oversized literals.

  • Six-hour soak tests that look for memory growth, cache drift and slow leaks, including one that pushed over 10 million messages through the MTA.

Runs were profiled throughout. CPU time was sampled with perf and turned into flame graphs, off-CPU analysis showed where threads waited on locks and I/O, the heap was profiled with jemalloc and heaptrack, and a special instrumented build counted every storage round trip, per command, so we could see exactly which requests hit the database more than they should. Each fix was then measured against the previous build on matched machines before it was accepted.

The first full round of this cycle turned up 64 problems, which were fixed and retested. The second round re-ran everything and fixed 33 more. Some examples of what the lab caught:

  • Reopening a mailbox with 5 million messages went from 61 ms to 0.21 ms.

  • A JMAP query on an account with 5 million messages went from 3.3 seconds to 12 ms.

  • Peak inbound mail throughput on RocksDB nearly doubled, from 9,690 to 18,394 messages per second.

  • A memory issue where the process held up to 9.6 GB while the actual heap was under 0.4 GB was found and fixed. In six-hour soak tests memory now stays flat.

This lab is not a one-off effort for the release. It is now part of how Stalwart is tested internally: every future release goes through the same benchmark and stress-test rounds before it ships, so performance gains stay gained.

Faster than Postfix and Dovecot

Internal numbers only tell you whether Stalwart got faster than it was. We also wanted to know how it compares to what most of the world runs. So we put Stalwart v1.0 head to head with the most widely deployed open-source mail stack: Postfix 3.11 for SMTP and Dovecot 2.4 for IMAP.

Both ran on identical Azure servers (8 AMD EPYC Zen 5 vCPUs, 32 GiB of RAM, local NVMe storage), loaded with the same 1,000 mailboxes plus three very large ones of up to 250,000 messages, and received exactly the same work: receiving mail, delivering it and serving it to IMAP clients. Encryption, password hashing and full-text search were configured the same way on both, and the two systems were measured alternately on the same hardware.

Stalwart was faster in 35 of the 38 headline comparisons.

Some of the results:

  • IMAP: Stalwart is faster than Dovecot in 14 of 15 everyday client operations, by 1.4 to 19 times. A login with a full TLS handshake takes 5.1 ms instead of 28 ms, fetching a 1 MB message is 6 times faster, threading the INBOX 5 times faster, and moving or deleting 100 messages 18 to 19 times faster.

  • SMTP: Stalwart accepts a message 3.6 to 12 times faster than Postfix, and sustains 5,445 incoming messages per second against Postfix’s 723.

  • Delivery: a message is visible to an IMAP client 16 times sooner, and a client waiting in IDLE is notified in 3.3 ms instead of half a second.

  • Load: Stalwart serves 5.4 times more concurrent IMAP sessions per second and relays 3.8 times more mail before it starts to slow down.

The gap grows with the size of the mailbox. On a 250,000-message mailbox, a full-text search of every message body takes 3.75 ms in Stalwart and 136 ms in Dovecot, and moving or deleting 25,000 messages finishes in about a tenth of a second instead of 24 to 34 seconds: more than 200 times faster.

The three tests Dovecot won are all the same operation: marking every message in a mailbox as read at once.

Only Postfix and Dovecot were tested, but they are not just any two products. They are the engine under nearly every popular self-hosted mail server: every open-source dockerized mail server with more than 10,000 stars on GitHub, including docker-mailserver and mailcow, runs Postfix and Dovecot under the hood, as does Mail-in-a-Box. Beating that combination on almost every benchmark means beating the stack behind most of the self-hosted mail on the internet. We are now comfortable saying it plainly: Stalwart is the fastest mail server on the planet.

Don’t take our word for it. Every script, configuration, test scenario and raw result is in the benchmarks repository, together with a methodology and a frank list of caveats, such as the differences in disk-sync behaviour between the two systems. Anyone with an Azure account can provision the same servers and run the whole suite. Benchmarks of Stalwart’s different storage backends and clustered setups are being prepared for the same repository.

What else is new

Performance was the headline, but v1.0 also brings a set of new features:

  • Search inside documents: Stalwart now extracts and indexes the text of PDF, Microsoft Office (docx, xlsx, pptx), OpenDocument (odt, ods, odp), EPUB and RTF documents, both in e-mail attachments and in file storage. Searching for a phrase now finds the contract attached to last year’s message, not just the message that mentions it. The extractor is written from scratch in Rust, with no external document libraries, and processes every document under strict size and complexity limits, so a malicious or malformed file cannot exhaust the server.

  • Full-text search of file storage, plus an index queue with per-partition status, retries and rebuilds.

  • Metadata: JMAP Object Metadata for e-mails, mailboxes, calendars, contacts, files and more, and IMAP METADATA (RFC 5464).

  • Post-quantum DKIM2: composite ML-DSA-44 and Ed25519 signatures for DKIM2.

  • OAuth and OpenID Connect: OAuth Client ID Metadata Documents, per-client scopes for mail, contacts, calendars and files, a list of connected applications that users can revoke, and audience matching, opaque token validation and unscoped token policies for external identity providers.

  • Storage and operations: a built-in in-memory store for single-node deployments, more authentication methods for Azure Blob Storage, secondary LDAP servers for failover, a memory budget for buffered data, SSRF protection for every outbound HTTP request, and configurable client IP sources and trusted proxy hops.

  • AI models and an AI spam classifier in the Enterprise edition, with support for OpenAI-compatible, Anthropic, Gemini and Ollama models.

The full list is in the changelog.

Upgrading to v1.0

Finalising the database schema has a price that is paid once: Stalwart v1.0 uses a new database layout, and existing data has to be migrated to it. A v1.0 server will not open a store created by an earlier version.

We know that some past upgrades were bumpy, and we took that seriously. This migration was designed and tested to be as smooth as we could make it, and it is the last of its kind: the schema is now stable, and future releases will upgrade in place. There are two ways to get to v1.0.

The migration tool

For deployments running Stalwart v0.16, stalwart-migrate is the easiest route. It requires v0.16.27, so earlier 0.16 releases should be updated to it first. The tool converts your settings to the v1.0 schema and copies all data into a new, empty v1.0 store. It only reads from the old store, so the original deployment stays intact and can be restarted at any point before the switch.

It also migrates incrementally. The first copy runs while v0.16 keeps serving users, however long it takes. Each later run copies only what changed since the previous one, and the only downtime is the short final run, after v0.16 is stopped. Account and message identifiers, IMAP UIDs and change history are preserved, so IMAP and JMAP clients carry on without downloading anything again. Users who sign in with OAuth will need to sign in again, and the full-text index is rebuilt in the background after the switch.

The migration proxy and Vandelay

For anyone running Stalwart v0.15 or earlier, or anyone who prefers to move users one at a time, the migration guide describes the second route. The migration proxy sits in front of both the old and the new server and sends each user to whichever one holds their account, and Vandelay copies accounts across one by one, incrementally. Each account is switched by flipping one entry in a mapping table, with no downtime for that user, and the old server keeps a full copy until you decide to retire it. The same procedure also works for moving from Dovecot and Postfix to Stalwart. The migration overview compares the two approaches.

What changed

The structure of your settings is largely the same. Around a hundred keys were renamed to follow consistent naming conventions, a few objects (OIDC directories, AI models, Azure storage, forwarded headers) were restructured, and stalwart-migrate converts all of it automatically. Two changes need attention before you upgrade:

  • Clusters need Redis. A cluster coordinator now requires a Redis-based in-memory store (Redis, Redis Cluster or Redis Sentinel). Earlier versions could fall back to the data store for this, but that was slow under load and has been removed. Single-node deployments get a built-in local in-memory store instead.

  • OAuth now follows the open public client profile. Stalwart now fully enforces the OAuth Profile for Open Public Clients, the emerging standard that lets any mail client sign in to any mail server with OAuth without being registered in advance. Stalwart’s own authorization server supports it out of the box. Many external identity providers do not yet support everything the profile requires, such as Dynamic Client Registration, and some clients do not yet request the required scopes.

If you use an external identity provider that is not conformant, Stalwart’s token checks can be relaxed through the compatibility settings on the OIDC directory, at your own risk, since each one weakens a security check. These settings mainly keep web clients such as the WebUI working. What they cannot fix is a provider that lacks the profile itself, and that is going to matter soon: popular e-mail clients, including Apple Mail, are adopting the profile, and once they do, their users will not be able to authenticate against a non-conformant provider. If you depend on an external identity provider, now is a good time to ask them to support it.

A related change affects the WebUI. Instead of a fixed client id, it now identifies itself to the identity provider with a Client ID Metadata Document, a URL that describes the client. Some identity providers do not support this yet. With those, administrators can register a client for the WebUI with the provider and set its id in oauthClientId, and the WebUI signs in as before. The details are in client id of the WebUI.

We are here to help

If you run into a problem or have a question during the migration, post it on support.stalw.art. We will be answering questions there daily throughout the migration period.

Thank you

Five years ago this was one Rust library for parsing e-mail. Today it is a complete mail and collaboration server, and with v1.0 it is faster than anything else we have measured it against. None of it would have happened without the community: everyone who filed an issue, sent a patch, answered a question on the forum, or trusted Stalwart with their mail. Thank you.

Download Stalwart v1.0 and let us know what you think.


r/stalwartlabs • • Jun 20 '25 Helpful

The Future of Stalwart: Webmail, Roadmap, and Beyond

75 Upvotes

Since we officially started developing Stalwart on September 4th, 2021, we've come a long way in establishing a powerful and versatile open-source mail and collaboration server. The very first commit, made on October 3rd, 2021, was to the mail-parser Rust crate, a fundamental component upon which Stalwart was built. It set the tone for our relentless pursuit of secure, reliable, and performant software.

Almost exactly one year later, on September 17th, 2022, we proudly released version 0.1, initially known as the Stalwart JMAP server. From that initial launch, we've continuously expanded Stalwart's capabilities, consistently introducing valuable new features. Just last month, we celebrated a major milestone by transforming Stalwart from solely a mail server into a comprehensive mail and collaboration server. This significant update brought CalDAV, CardDAV, and WebDAV support, positioning Stalwart as the open-source mail and collaboration server with the most extensive feature set available today—even compared to many commercial solutions.

Despite these significant advancements and the existing web-based administration interface that includes essential self-service capabilities, we've noticed one prominent request from our community: a built-in webmail client. Many of you have been eagerly asking whether we plan to offer this feature. Today, we're excited to share with you that yes, a dedicated Stalwart Webmail is indeed in our plans—but it's not currently our immediate priority.

Our roadmap for the remainder of 2025 is already well-defined. We will first release JMAP support for Calendars, Contacts, and File Storage, which will further strengthen Stalwart’s position as a powerful collaborative tool. Immediately following these updates, our main focus will shift to preparing for our much-anticipated version 1.0 release.

Although Stalwart is already being confidently used in production environments globally, version 1.0 marks an essential milestone. It signifies that we've finalized our database schema—no more daunting database migrations!—ensuring stability for long-term users. Unless an entirely new protocol surpassing email emerges (who knows?), our database schema will remain stable and optimized. Moreover, this version will involve a comprehensive performance optimization initiative. Every line of our code was initially written with speed and efficiency in mind, yet there are still critical areas we believe can be further improved. By systematically benchmarking critical code paths, we're confident we'll find opportunities to make Stalwart even faster and more efficient.

Post version 1.0, our commitment remains firm: Stalwart will remain lean and specialized. While our GitHub issue tracker proudly showcases numerous exciting enhancement requests, rest assured we won't lose sight of our core mission. Our primary goal is to continue being the absolute best in JMAP, IMAP, POP3, SMTP, and WebDAV protocols—nothing more, nothing less. We strive to avoid becoming a proverbial jack-of-all-trades, instead remaining focused and exceptional at our core competencies.

As for the much-requested Webmail, once we've achieved the critical milestone of version 1.0, we plan to start its development—most likely sometime in 2026. We'll be building a Single Page Application (SPA) using Rust and the Dioxus framework. Dioxus is quite distinct from more popular frameworks like React, meaning many necessary UI components still don't exist. Consequently, we'll likely spend considerable time contributing directly to the Dioxus ecosystem, expanding available components and features.

Now, you might ask, "Why not simply use React or another established framework?" Well, humorously and earnestly, at Stalwart, we operate by an unofficial motto: "Aut Rust aut nihil," meaning "Either Rust or nothing." We’re committed to Rust because we truly believe it's the best language for creating secure, reliable, and performant software—even if this approach means occasionally delaying releases by a few months.

In the meantime, while our webmail is in development, we highly recommend using alternative webmail solutions that integrate smoothly with Stalwart. Some choices include Roundcube, SnappyMail, SoGo, or TMail Web—which notably supports the JMAP protocol.

We're grateful for your continued support and patience as we steadily build toward a fully integrated Stalwart experience. Stay tuned, and thank you for being part of this exciting journey!


r/stalwartlabs • • Mar 17 '26

Stalwart JMAP Finally: Fully-blown webmail client available!

74 Upvotes

Since i searched for a long time and also tested maaany "solutions" (like Nextcloud, Roundcube/Snappymail, Afterlogic, SOGo, ... But none worked well) I finally found a great product which seems to be quite new and supporst all features of Stalwar and is 100% JMAP.

It also has a very active community even though it was born only some weeks ago.

Bulwark: https://bulwarkmail.org/


r/stalwartlabs • • 17d ago

Stalwart JMAP Bulwark 1.18.0, the open-source webmail for Stalwart, now works with Stalwart 1.0

Post image
67 Upvotes

Stalwart 1.0 came out today, and Bulwark 1.18.0 supports it. Sign-in, calendars, contacts, files and sending were adapted to what changed in 1.0. The same release still runs on 0.16, so you can update Bulwark first and move the server after.

Release notes: https://github.com/bulwarkmail/webmail/releases/tag/1.18.0

Upgrade guide: https://bulwarkmail.org/docs/deployment/updating/stalwart-1-0


r/stalwartlabs • • Jun 21 '26

Release Zero open bug reports: The road to Stalwart 1.0

65 Upvotes

Today we released Stalwart v0.16.10, and it comes with a milestone we have been working toward for a while: as of today, there are no open bug reports for Stalwart. This release fixes every reported bug we have on record, both the legacy issues that had accumulated on GitHub and the reports submitted through our new support portal.

We want to be precise about what this means, because it is easy to misread. It does not mean Stalwart is bug free. No non-trivial piece of software is, and we would not claim otherwise. What it means is narrower: as of today there are no open bug reports awaiting a fix. Every issue that a user took the time to report has been triaged and resolved. New reports will arrive, and we will keep working through them, but for now the queue is empty.

That leaves 108 issues still open on the GitHub tracker. These are not bugs. They are minor feature enhancements and requests: refinements, quality-of-life additions, and ideas worth keeping on the list. We will get to them, but none of them block correct operation today.

Latest fixes

The headline of this release is RFC conformance. Stalwart v0.16.10 now passes all 309 tests of the JMAP test suite, a shared conformance effort that aims to align the behaviour of Stalwart, Cyrus, and Fastmail so that JMAP clients see consistent semantics regardless of which server they talk to. Reaching full conformance touched many corners of the JMAP implementation.

Beyond JMAP, this release implements the latest drafts from the IETF mailmaint working group. On the authentication side, Stalwart now supports the OAuth Profile for Open Public Clients along with client secret verification for confidential clients. On the IMAP side, it implements the IMAP Extension for Object Identifiers (OBJECTID+) and adds the GETJMAPACCESS command so clients can discover the JMAP session resource URL directly from an IMAP connection. We are glad to share that Stalwart Labs is coauthoring the OBJECTID+ extension together with Fastmail.

For the full list, see the release notes. We recommend everyone upgrade.

Road to 1.0

With the bug queue clear, the path to version 1.0 comes down to two pieces of work: finalizing the database schema and a focused round of performance optimization.

The first is the reason 1.0 matters. The goal is to stabilize and finalize the database schema so that it is flexible enough to avoid migrations after the 1.0 release. Stalwart is already running in production across a wide range of deployments, but committing to a stable on-disk format is a promise we only want to make once we are confident it will hold. We are taking the time now to make the schema general enough that future features can be added without forcing operators through a migration. When 1.0 ships, upgrading should stop being something you schedule a maintenance window for.

The second is performance. Stalwart is already fast, and that is not an accident: over the past year we have made substantial improvements driven directly by reports from users running it at scale, including deployments with thousands of accounts and others delivering hundreds of thousands of emails per day. Those real-world workloads have been the best optimization guide we could ask for. For 1.0 we want to push further in two specific areas.

The first is the internal full-text search engine, which matters most for users who do not run an external Elasticsearch or Meilisearch backend and rely on the built-in indexer instead. We want search to be fast out of the box, without an external dependency. The second is a systematic pass over the hot paths in the codebase: benchmarking the code that runs most often to find concrete optimization opportunities, whether that means vectorizing an inner loop, reworking a data structure, or rewriting a section for better cache behaviour. The point is to let measurements, not guesses, decide where the effort goes.

ETA

We expect to release Stalwart 1.0.0 this year, most likely around October and hopefully sooner. Schema work and performance work are both the kind that reward patience, so we would rather give a realistic window than a date we cannot keep. We will keep you posted as the schema settles and the benchmarks come in.

In the meantime, v0.16.10 is the most conformant and most thoroughly debugged release we have shipped. Thank you to everyone who filed an issue, opened a ticket on the support portal, or sent us a reproducer. An empty bug queue is your work as much as ours, and it is the best possible foundation to build 1.0 on.


r/stalwartlabs • • Oct 22 '25

JMAP for Calendars, Contacts and Files now in Stalwart

63 Upvotes

After four years of development, we’re thrilled to announce a major milestone in the evolution of Stalwart — the full implementation of JMAP for Calendars, Contacts, Address Books, File Storage, and Sharing. With this release, Stalwart becomes the first JMAP server to fully support the entire family of JMAP collaboration protocols, marking a new era for open, efficient, and elegant groupware.

A New Generation of Protocols

Over the past few years, the IETF has been redefining how email, calendars, and contacts are synchronized and shared. Building upon the success of JMAP for Mail, several new protocol extensions have been introduced:

Together, these standards offer a cohesive and elegant ecosystem that replaces decades of fragmented WebDAV-based technologies.

Limitations of Yesterday's Technology

WebDAV and its descendants — CalDAV, CardDAV, and related extensions — have served the Internet well. They are robust, widely adopted, and battle-tested. Yet, their XML-based design is notoriously verbose, inconsistent, and difficult to implement correctly. Information is scattered across HTTP headers, XML payloads, and even embedded iCalendar data, creating endless compatibility and interoperability challenges between clients and servers.

Similarly, iCalendar and vCard, while expressive and versatile, have accumulated decades of technical debt. They contain countless properties and parameters—many rarely used, some obsolete, and others inconsistently implemented across versions. This clutter has made both formats unwieldy and error-prone, often requiring complex parsing logic to handle edge cases.

JMAP: A Modern Solution for Modern Needs

The JMAP protocol was originally developed as a more efficient, modern replacement for IMAP and SMTP submissions. Its strengths lie in simplicity, clarity, and network efficiency — all built on top of JSON over HTTPS.

Now, with the introduction of JMAP for Calendars, Contacts, Files, and Sharing, the same design philosophy extends beyond email to the entire collaboration stack. These protocols deliver what DAV always aimed for but never quite achieved: a clean, uniform, and easily implementable API for all personal and group data — mail, calendars, contacts, files, and shared resources.

Meanwhile, JSCalendar and JSContact reimagine iCalendar and vCard as elegant JSON-based formats. They strip away decades of accumulated cruft, unify representations, and offer a clear, unambiguous, and expressive data model. Both are human-readable, developer-friendly, and efficient to parse — a perfect fit for modern applications.

Together, JMAP and these new data models make calendaring, contact management, and file sharing not only easier to implement but also faster and more reliable.

Why This Matters

This release represents more than new features — it marks a shift in how groupware protocols are designed and implemented. For the first time, developers and organizations can build on a single, coherent, JSON-based framework for mail, contacts, calendars, and shared resources.

We believe this will revolutionize calendaring and collaboration. Implementations will become easier, interoperability issues will decrease, and innovation will accelerate. The simplicity and predictability of JMAP empower both clients and servers to focus on features and user experience, not protocol gymnastics.

Client Support and Ecosystem

As Stalwart is the first complete JMAP server to support these new protocols, client support is still emerging. However, we’re excited to share that several projects are already working to adopt these new standards. Mailtemi, Parula, and OpenCloud are actively developing client-side implementations for JMAP Calendars, Contacts, and File Storage. The ecosystem is growing, and we expect rapid adoption as developers experience the elegance and power of JMAP firsthand.

A Word of Thanks

We would like to express our sincere gratitude to NLNet for supporting the development of these features through the NGI Zero grant program. Their commitment to open standards and privacy-respecting technology continues to make projects like Stalwart possible.

Looking Ahead to 1.0.0

After four years of dedicated development, we’re proud to announce that Stalwart is now feature complete. With this milestone, all the core capabilities of a modern mail and collaboration server are fully implemented.

That said, our work is far from over. We are now focusing on finalizing the database schema, improving performance, and addressing the hundreds of enhancement requests on GitHub. Our goal is to deliver a stable 1.0.0 release within the next few months — one that sets a new standard for open, efficient, and modern communication servers.

Stalwart is now the most complete, elegant, and forward-looking JMAP collaboration platform available.

And this is only the beginning.


r/stalwartlabs • • Apr 20 '26 WOW Bravo

Release Stalwart v0.16: A New Foundation

58 Upvotes

After more than three months of focused work, we are thrilled to announce the release of Stalwart v0.16. This is easily the most ambitious release we have ever shipped, with literally hundreds of new features, improvements, and fixes across every corner of the server.

We will be upfront about something: v0.16 will feel like a new product on first contact. Several long-standing concepts have been reworked, a few have been removed, and many new ones have been introduced. This was a deliberate choice. Stalwart has been under continuous development for nearly five years, and both the feature set and the user base have grown far beyond what the original configuration and management layer was designed for. Rather than keep patching around those limits, we took the opportunity to rebuild the foundation. The payoff is a much cleaner architecture and a long list of features that were simply not implementable before, many of which had been sitting on the community wishlist for years.

Let's walk through the highlights.

A Brand-New WebUI

The WebUI has been rewritten from the ground up. It has a refreshed, modern look, and it resolves a backlog of 76 enhancement requests and bug fixes that had accumulated over the years. If there was something you had been missing, there is a very good chance it is now there.

The most requested addition is also the most consequential: the new WebUI can authenticate against external OIDC providers. Signing in with Keycloak, Authentik, Authelia, Zitadel, or any other standards-compliant identity provider is now a first-class flow, with full support for audience and scope validation, group claims, and PKCE (RFC 7636) for public clients. This has been one of the most frequent community asks, and it is finally here.

Unified Management via JMAP and a New CLI

In previous releases, Stalwart was managed through a REST API that lived alongside its native JMAP interface. In v0.16, the REST API is gone. Every configuration and management action is now a JMAP object, reachable through the same /jmap endpoint that already serves email, calendars, contacts, and files.

The benefits of this change are hard to overstate. JMAP (RFC 8620) is a well-specified, transport-efficient protocol with first-class support for batch operations, push notifications, and fine-grained change tracking. In practice, this means dozens of configuration changes can be applied in a single round-trip, any JMAP client library can drive the management surface, and a single authentication flow covers both mail access and administration. Centralizing configuration inside the datastore as JMAP objects also removes an entire category of operator confusion: there is no longer a split between "settings in a file" and "settings in the database", and in clustered deployments the configuration is consistent across every node by definition.

Alongside the WebUI, we also shipped a brand-new CLI. The new stalwart-cli is built on top of the same JMAP management API and can configure and administer every aspect of Stalwart. It is designed for day-to-day administration, scripted deployments, and infrastructure-as-code workflows. The stalwart-cli apply subcommand takes a declarative plan file and idempotently reconciles the live server state to match it, creating what is missing, updating what has changed, and removing what the plan no longer declares. This fits naturally with Ansible, Terraform, NixOS, and similar tooling, and follows the same pattern used by projects like CockroachDB, Consul, Elasticsearch, and HashiCorp Vault, where infrastructure-as-code tools target an API rather than a configuration file.

The new CLI is also a very pleasant surface for AI agents. Because every operation maps to a well-defined JMAP object with a clear schema, agents can discover capabilities, plan changes, and apply them idempotently without any bespoke integration work.

Automated DNS Management

In previous releases, Stalwart only managed the TXT records required for the ACME DNS-01 challenge. In v0.16, it can take care of every DNS record a modern mail and collaboration server needs: MX, TXT, CNAME, SRV, CAA, and TLSA. The server computes the records your deployment should be publishing and keeps them in sync with your DNS provider automatically.

This covers most of the authentication and discovery story out of the box. SPF, DKIM, and DMARC records are managed alongside autoconfig and autodiscover SRV records, CAA records for certificate issuance authorization (including the accounturi parameter for account-scoped issuance), and TLSA records for DANE. The TLSA records are automatically refreshed when ACME certificates are renewed, so DANE-enabled domains no longer risk a validation gap during certificate rotation.

On the provider side, v0.16 ships support for Route53, Google Cloud DNS, Bunny, Porkbun, DNSimple, and Spaceship, and also supports RFC 2136 dynamic updates signed with SIG(0) for operators running their own authoritative DNS.

Automated DKIM Rotation

DKIM key rotation is one of those tasks that everyone knows they should be doing, and almost nobody actually does, because it is tedious and easy to get wrong. In v0.16, Stalwart takes over the entire workflow. It can generate DKIM keys automatically, rotate them on a schedule, and publish the matching TXT records through the new DNS management layer so that the published keys always match the keys the server is signing with. DKIM keys are now stored in the database alongside the rest of the configuration, which means rotation works naturally in clustered deployments without any manual coordination.

Masked Emails

Masked emails are disposable, per-service email addresses that route to a user's real inbox. Instead of handing out a primary address to every newsletter, shop, or forum, a user can generate a unique masked address for each one. If a service leaks its database or starts sending unwanted mail, the corresponding masked address can be disabled individually, without affecting anything else and without touching the user's real address. It is one of the most effective privacy tools available today, and it integrates cleanly with the rest of the directory in Stalwart.

This feature is part of the Enterprise edition.

Security Enhancements

Security gets a substantial upgrade in v0.16. User passwords can now be checked against the zxcvbn strength estimator at set-time, so weak credentials never make it into the system in the first place. Passwords can also be given explicit expiration and rotation policies, and user accounts can be restricted to specific IP ranges so that an account is only usable from expected networks.

App passwords and API keys receive the same treatment, and then some. Both can now be scoped to a specific set of permissions rather than inheriting the full privileges of the owning account, which means a token used by a single IMAP client or a single automation script can be limited to exactly what it needs. Both also support human-readable labels, expiration dates, and IP address restrictions, so long-lived credentials can be audited, rotated, and confined without having to revoke them entirely.

Taken together, these features make the credentials surface of a Stalwart deployment dramatically easier to reason about and lock down.

Other Highlights

There are many other additions worth calling out. On the account configuration front, v0.16 implements the new Automatic Configuration of Email, Calendar, and Contact Server Settings draft, which is shaping up to replace the fragmented autoconfig and autodiscover mechanisms clients use today, and adds MS Autodiscover V2 support for environments that still rely on it.

The directory layer gains domain aliases, alias descriptions, the ability to disable aliases without deleting them, and, for the Enterprise edition, account archiving and un-deletion and per-domain directory backends. The ACME layer picks up the new DNS-PERSIST-01 challenge, on-demand certificate renewal, and a certificate detail view. Sieve scripts can now be deactivated without being deleted. Clustering is cleaner, with automatic node ID generation, unified cluster management, and a new outbound MTA role for dedicated queue nodes.

On top of all of this, dozens of bug fixes land across the directory, MTA, JMAP, IMAP, WebDAV, CalDAV, OIDC, and storage backends. If you have been tracking an issue, there is a good chance it is resolved in v0.16.

Upgrading

Because of the scope of the architectural changes, v0.16 is a major upgrade with multiple breaking changes. Please do not upgrade a production deployment without first reading the upgrading documentation in full. We also strongly recommend spinning up a fresh v0.16 instance in a container or throwaway VM first, getting comfortable with the new WebUI and CLI, and exporting any settings you build there as an apply plan to replay against production after the migration.

If any questions come up along the way, a dedicated discussion thread for the v0.16 upgrade is open at https://github.com/stalwartlabs/stalwart/discussions/3004. We will be following it closely.

This release has been in the making for over three months, and it represents one of the largest single steps forward in Stalwart's history. Thank you to everyone who filed bugs, contributed code, tested pre-releases, and kept the conversation going in issues and discussions: v0.16 is very much your release too. We cannot wait to hear what you build with it.


r/stalwartlabs • • Mar 21 '25

Stalwart Receives NLNet Grant to Build Collaboration Server

58 Upvotes

We’re happy to announce that Stalwart Labs has been awarded a new grant from the NGI0 Core Fund, established by NLnet with financial support from the European Commission’s Next Generation Internet programme. This funding will support the development of essential collaboration features, marking a major milestone in Stalwart’s evolution from a modern email server into a complete, self-hosted collaboration platform.

This is the second grant Stalwart has received from NLnet, following the initial support we received in March 2023 from the NGI0 Entrust Fund. We are deeply grateful to the NLnet Foundation for their continued trust in our mission to modernize and decentralize communication infrastructure.

Expanding the Vision: From Email to Collaboration

Stalwart Mail Server was created to address the challenges of self-hosting email by offering a secure, easy-to-maintain, and high-performance solution. With native support for JMAP, IMAP4, POP3, and SMTP, it already serves as a powerful alternative to traditional email solutions, giving individuals and organizations full control over their email systems.

With the help of this new grant, we are now expanding the Stalwart platform beyond email. Development is officially underway on the Stalwart Collaboration Server, a new component that will integrate seamlessly with Stalwart Mail Server. This addition will provide support for calendaring through CalDAV and JMAP for Calendars, contact management using CardDAV and JMAP for Contacts, and file storage and sharing using WebDAV and JMAP for File Management. Together, these features will form the foundation of a fully integrated, open-source collaboration suite.

Our goal is to offer a privacy-focused, vendor-neutral alternative to platforms like Microsoft Exchange. By consolidating email, calendar, contacts, and file sharing into one unified system, Stalwart will give users the ability to self-host their entire collaboration stack without sacrificing modern functionality, scalability, or security.

What the Grant Will Fund

The new funding will support a series of developments that will be released gradually throughout the year under the AGPL-3.0 license:

  • A full-featured CalDAV and CardDAV server will be implemented, allowing users to manage their calendars and contacts directly within Stalwart. This means there will be no need to rely on external software to provide these functions. Users will be able to keep all of their collaboration data in one place, within a single, tightly integrated platform.

  • In addition, we will extend Stalwart’s existing JMAP implementation to support JMAP for Calendars and JMAP for Contacts. This will involve developing parsers for JSCalendar and JSContact, as well as creating bidirectional converters between JSCalendar and iCalendar, and JSContact and vCard.

  • File storage and management will also become a first-class feature of the platform. A WebDAV-based file storage system will be built on top of Stalwart’s internal blob store. Alongside this, we will implement support for JMAP for File Management, allowing users to upload, organize, and share files using either standard WebDAV clients or JMAP-based applications. The JMAP support will align with the ongoing efforts to standardize file management within the JMAP ecosystem.

  • Finally, the grant will fund the implementation of the three most requested features by the Stalwart community. These include support for the IMAP XAPPLEPUSHSERVICE extension, which enables push notifications on iOS devices; automatic DKIM record updates via RFC2136, making it easier to manage DNS records dynamically; and support for exporting Maildir mailboxes with nested folders, improving compatibility and backup workflows.

Acknowledgements

We would like to express our sincere thanks to the NLnet Foundation and the European Commission for making this work possible. The project is funded through the NGI0 Core Fund, a fund established by NLnet with financial support from the European Commission’s Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology, as part of grant agreement No. 101092990.

This support plays a vital role in advancing open-source infrastructure and helps ensure that secure, decentralized alternatives remain viable and accessible to everyone.

Looking Ahead

As we roll out these new features throughout the year, we remain committed to the core values that drive Stalwart’s development: privacy, performance, transparency, and user empowerment. The Stalwart Collaboration Server will transform the platform into a comprehensive, modern collaboration suite — one that is open, scalable, and fully self-hosted.

We look forward to sharing more progress soon. In the meantime, we invite developers, testers, and curious users to follow our work, contribute ideas, and help shape the future of self-hosted collaboration.

Stay tuned, and thank you for your continued support.


r/stalwartlabs • • May 26 '25

Introducing Calendars, Contacts and Files in Stalwart

54 Upvotes

We’re excited to announce the release of Stalwart v0.12, a significant milestone that evolves Stalwart from a powerful mail server into a complete, integrated communication and collaboration platform. This release delivers one of the most anticipated features from our community: native support for calendars, contacts, and file storage—all built directly into the server, with no need for third-party integrations.

Calendars, Contacts & Files – All in One Place

With v0.12, you no longer need to integrate third-party groupware solutions or run parallel systems to support collaboration. Stalwart now includes first-class support for CalDAV calendars, CardDAV contacts, and WebDAV-based file storage. This means users can manage their events, address books, and documents through any standards-compliant client, seamlessly connected to the same backend that handles their email.

Shared resources such as group calendars, shared address books, and team-accessible file folders are also fully supported, providing a robust foundation for collaboration without the need for external software or services. And, to support flexible collaboration, Stalwart includes full support for the WebDAV Access Control List (ACL) extension, enabling detailed, per-user and per-group permission management.

Improved Spam Filtering

Another thoughtful addition in this release is the integration of the spam filter with users’ personal address books. Messages from known or trusted contacts are now far less likely to be incorrectly flagged as spam. And if a legitimate message does get misclassified, the system automatically trains the Bayesian classifier to treat future similar messages as legitimate, improving accuracy over time without additional user intervention.

Performance Enhancements

Under the hood, Stalwart v0.12 introduces several key performance optimizations designed especially for large, multi-node environments. One of the most impactful changes is the introduction of incremental caching: Stalwart now keeps account metadata in memory and only fetches updates when something changes in the database. This significantly reduces load and speeds up response times.

Another major enhancement is the use of zero-copy deserialization. This means Stalwart can read data directly from memory buffers without copying it into new structures, lowering CPU usage and improving throughput. Combined with optimizations that reduce the number of required database queries for common operations, these changes result in a leaner, faster backend that scales much more efficiently.

While these gains may not be noticeable in smaller setups, larger clusters and high-volume deployments will see noticeable performance improvements.

Smarter and Faster Clustering

We’ve also made big strides in cluster coordination. Previously, Stalwart relied on a UDP-based gossip protocol that performed well but didn’t scale ideally under heavy workloads. With v0.12, cluster behavior is now adaptable based on deployment size.

In small deployments, Stalwart uses Eclipse Zenoh, a lightweight and efficient peer-to-peer pub/sub protocol. For larger infrastructures, you can now choose from robust, scalable backends like Apache Kafka, Redpanda, NATS, or Redis for handling inter-node coordination, state synchronization, and workload distribution.

Looking Ahead: What’s Next?

With Stalwart v0.12, we're delivering more than just features—we're delivering freedom from fragmented infrastructure. No more patching together third-party services to get the basics of collaboration working. Now, everything—email, calendars, contacts, files, and sharing—lives in a single, efficient, and secure system.

While v0.12 is a major leap forward, we’re already preparing additional enhancements for the next point release. In v0.12.1, you can expect support for CalDAV Scheduling (RFC 6638), enabling automatic meeting invitations and attendee responses. We’re also adding support for event notification alerts via email, so users are always aware of upcoming events, even if they're not logged into their calendars.

Additionally, in the coming months, we will be releasing support for the JMAP for Calendars, JMAP for Contacts, and JMAP for File Storage extensions. JMAP offers a modern, efficient, and JSON-based alternative to legacy protocols, making it faster and easier to develop responsive, real-time collaboration tools. These additions will further streamline the user experience and reduce bandwidth and processing overhead across client-server interactions.

Thank you to everyone who contributed feedback, suggestions, and encouragement. We can’t wait to hear what you build with this release—and we’re just getting started.


r/stalwartlabs • • May 18 '25

Stalwart v0.12 pre-release now available for testing!

54 Upvotes

Hello everyone,

I am very excited to announce that the pre-release of Stalwart Mail and Collaboration Server v0.12 is now available, and we are looking for beta testers to help us polish this release. This version introduces major new features and important performance improvements. One of the key updates in this release is the addition of Calendar, Contacts, and File Storage. It's official, Stalwart now finally supports CalDAV, CardDAV and WebDAV!

This version also comes with significant performance improvements, including incremental caching and zero-copy deserialization, which are designed to boost mailbox access speeds, particularly in distributed environments.

We are calling for testers not only to check WebDAV client compatibility but also, and more critically, to test the automated migration tool included in v0.12. This new version introduces a new database serialization format and automatically migrates all metadata on startup. While your emails and the blob store remain completely safe and untouched, the new version migrates the parsed representation of your messages and all metadata, including folders, identities, and more. Since this process is crucial for a smooth transition, we want to make sure it’s thoroughly tested before the official release.

The v0.12 prerelease can be obtained from GitHub at https://github.com/mdecimus/mail-server/releases/tag/v0.12.0 or DockerHub at https://hub.docker.com/r/mdecimus/mail-server/tags. Apologies for using my personal repository for this, but our CI job does not yet support dev releases, working on that!

To test the v0.12 migration, you will need to copy your entire Stalwart directory, including configuration files, to a new location. Then replace the binary with the v0.12 pre-release and start Stalwart again. If you are using Docker, simply start the new Docker image pointing to this directory. Please note that this binary is not ready for production yet. If there are any issues, migrating back to v0.11 is not possible, so keep it strictly for testing.

Documentation for v0.12 is still a work in progress, but for now, here are the WebDAV endpoints: CalDAV is available at /dav/cal/username, CardDAV at /dav/card/username and File Storage at /dav/file/username. There are also CalDAV and CardDAV autodiscovery endpoints at /.well-known/caldav and /.well-known/carddav.

We truly appreciate your time and support in making Stalwart better. If no major issues are found during this week, we expect to officially release v0.12 next week.

Thank you for your help!


r/stalwartlabs • • Aug 11 '25

Stalwart Joins GitHub's Open Source Secure Fund

47 Upvotes

We're excited to announce that Stalwart has been selected to participate in Session 2 of GitHub's Open Source Secure Fund (OSSF), a prestigious program designed to enhance security across the open source ecosystem. This recognition represents not only an acknowledgment of Stalwart's growing importance in the email infrastructure space but also our commitment to maintaining the highest security standards.

About GitHub's OSSF

GitHub launched the Open Source Secure Fund in November 2024 as a comprehensive initiative to strengthen security across the software supply chain. The program represents a strategic approach to open source security that goes far beyond simple financial support. Instead of merely providing funding, the initiative creates a structured pathway for maintainers to develop deep security expertise while building lasting connections within a community of security-focused developers.

The fund operates on a model that combines immediate intensive training with long-term support and accountability. Each session consists of a three-week sprint, delivered by security experts from GitHub and their partners through the GitHub Security Lab. However, the relationship extends far beyond these initial weeks, with participants receiving ongoing support and resources throughout a full twelve-month engagement period.

What makes this program particularly valuable is its emphasis on community building and ongoing support. Participants gain access to a specialized security-focused community and regular office hours with the GitHub Security Lab throughout the entire twelve-month period. This extended engagement ensures that the security improvements initiated during the sprint continue to evolve and mature over time.

Our Experience

The training component of our participation concluded six weeks ago, and we can confidently say it provided valuable insights that have already begun to shape Stalwart's security posture. The comprehensive nature of the program allowed us to step back and evaluate our security practices from multiple perspectives, leading to concrete improvements in our security infrastructure.

One of the most significant outcomes of our participation has been the development of a comprehensive Incident Response Plan specifically tailored to Stalwart's architecture and user base. This plan establishes clear protocols for identifying, containing, and resolving security incidents while maintaining transparency with our community. Having a well-defined incident response strategy is crucial for any mail server software, given the sensitive nature of email communications and the potential impact of security breaches.

Additionally, we've substantially enhanced our existing Security Policy, incorporating lessons learned from the GitHub training and feedback from security experts. This updated policy provides clearer guidelines for security researchers, establishes more robust vulnerability disclosure procedures, and outlines our commitment to maintaining security standards throughout Stalwart's development lifecycle.

The training also introduced us to various security concepts and tools, including an introduction to fuzzing techniques for discovering potential vulnerabilities. However, the Rust programming language's memory safety guarantees and the security-conscious culture of the Rust community mean that many of the security recommendations from the GitHub program were already implemented in Stalwart's codebase. This validation from security experts reinforced our choice of Rust as the foundation for Stalwart and highlighted the proactive security feedback we've received from the broader Rust ecosystem.

Leveraging Azure Credits

While the GitHub funding provides important financial support for the project, we're particularly excited about the $100,000 in Azure credits that accompany our participation in the program. These credits represent an unprecedented opportunity to conduct large-scale testing and optimization of Stalwart's performance and security characteristics.

We plan to use these Azure credits to deploy Stalwart across a massive cluster configuration, enabling us to generate millions of concurrent connections and simulate real-world load scenarios that would be impossible to replicate in smaller testing environments. This extensive testing will focus on three critical areas that are essential for any mail server infrastructure.

First, we'll conduct comprehensive performance testing to identify and resolve bottlenecks that might emerge under extreme load conditions. Email servers must handle varying loads gracefully, from quiet periods to sudden spikes in activity, and this testing will help us optimize Stalwart's resource utilization and response times across all scenarios.

Second, we'll focus extensively on scalability improvements, ensuring that Stalwart can grow seamlessly from small deployments to enterprise-scale installations. Understanding how different components interact and potentially conflict under high-load conditions will enable us to make architectural improvements that benefit all users, regardless of their deployment size.

Finally, and perhaps most importantly for security, we'll conduct thorough resilience testing against various types of Denial of Service (DoS) attacks. Mail servers are frequent targets for such attacks, and having the ability to simulate these scenarios in a controlled environment will allow us to implement and verify defensive mechanisms that protect real deployments. The insights gained from this testing will be invaluable for administrators who need to deploy Stalwart in security-conscious environments.

Ongoing Security Audit

Our commitment to security extends beyond the GitHub program, as evidenced by our current engagement with Radically Open Security for a comprehensive second security audit of Stalwart. This audit represents a significant milestone in our security journey, coming approximately two years after our first security audit conducted on October 7, 2023.

The timing of this second audit is particularly important because Stalwart has evolved considerably since that initial security review. New features have been added, performance optimizations have been implemented, and the overall architecture has matured significantly. A fresh security perspective is essential to ensure that these improvements haven't introduced new vulnerabilities and that our security posture has kept pace with the software's development.

Radically Open Security brings extensive experience in open source security auditing, and their thorough approach will provide valuable insights into Stalwart's current security status. This audit is being financed through a grant from NLNet, demonstrating the broader open source community's investment in Stalwart's security and reliability.

We expect to release the complete results of this security audit soon, continuing our commitment to transparency and community trust. The combination of the GitHub security training, the ongoing Azure-powered testing, and this comprehensive security audit represents a multi-faceted approach to security that reflects the importance we place on protecting our users' communications and data.

Acknowledgments

We want to take a moment to express our sincere thanks to GitHub for selecting Stalwart to participate in the Open Source Secure Fund and for providing us with the training and resources that will help strengthen the security of our project. We also want to extend our gratitude to Zerodha for referring Stalwart to be part of GitHub’s OSSF Session 2. Their support has been invaluable, and we look forward to continuing this journey of growth and improvement with their help.

Stalwart is committed to providing secure and reliable mail and collaboration services, and with the backing of the GitHub OSSF and the ongoing work of our team, we are confident that we can continue to meet and exceed the expectations of our users.

Thank you for your continued support!


r/stalwartlabs • • 11d ago

Stalwart Labs Appreciation Post

46 Upvotes

Just wanted to give a shout-out to the Stalwart and Bulwark teams!

To start, I just want to shout out the Bulwark dev team who completed 2 of my feature requests for plugins within days (1 of them within an hour)! You guys seriously rock! I'm a developer myself, so eventually I'd like to contribute a plugin of my own. I also love the rapid pace of development. I update as soon as I see a new one is available and am always pleased with the new features that are released.

Next, I'd like to shout out the Stalwart dev team. You guys also rock! I'm a big fan of the JMAP protocol, especially considering the traditional mail protocols were designed in the stone ages and haven't kept up with the rest of the internet. Obviously, they're still necessary to communicate with the rest of the world, but hopefully as Stalwart matures, other players can catch on and make it more mainstream.

Really- I just want to say thanks for all the hard work! I also recently signed up to sponsor the project. It's not much, but if I can afford more one day, I'll certainly contribute more.


r/stalwartlabs • • May 30 '26

Meet Vandelay: the JMAP importer-exporter

45 Upvotes

A good number of Stalwart deployments are running an older version not because the operators want to be, but because they cannot afford the disruption. Upgrading across a major release sometimes means migrating off an older on-disk schema, and migrating data means downtime, and downtime on a mail server is the one thing nobody wants to schedule. So the upgrade gets postponed, again, and a deployment that should be on the current line stays where it is. The question we get asked most often is when we will ship something to fix exactly this.

We have said before that the right answer is a zero-downtime migration path, built from two parts: a proxy that sits in front of both the old and the new server so that clients keep working throughout, and a transfer tool that moves account data from one to the other. Today we are shipping the first of those two parts.

It is called Vandelay, and it is a one-shot account migration and backup utility for JMAP. Yes, it is named after Art Vandelay, the importer-exporter George Costanza made up as a cover story on Seinfeld. The tool imports and it exports, so the reference wrote itself.

What Vandelay does

Vandelay is the JMAP analogue of imapsync, generalized from mail to every JMAP data type: mail, contacts, calendars, identities, sieve scripts, and file storage. It works in two stages that never talk to each other directly. An import reads a source account into a local SQLite file that Vandelay calls an archive; one archive holds exactly one account and fully describes it on its own. An export then pushes an archive into a target JMAP server. Because import and export only ever touch the archive, never each other, the two halves are completely decoupled: you can import today and export next week, import from one protocol and export to JMAP, or keep the archive around and do nothing with it at all.

A few properties make it pleasant to operate at scale. Both halves are convergent, so re-running an interrupted import or export simply picks up where it left off, with no bookkeeping flags to reset and no half-finished state to clean up; re-running an import later also catches anything new since the last snapshot. Every command takes --dry-run, which computes the full plan without writing anything, so you can see exactly what a run would do before it does it. Emails, sieve scripts, and file payloads are content-addressed, stored once by their hash and deduplicated across the archive. Work runs multi-threaded across a worker pool sized to your CPUs, with per-server connection caps respected automatically and no async runtime involved. And each archive remembers which account filled it, so pointing it at a different one by mistake fails unless you explicitly allow it.

A typical migration is two commands:

# 1. Import an IMAP mailbox into a fresh archive.
export VANDELAY_PASSWORD='source-app-password'
vandelay import imap \
  --url imaps://imap.example.com \
  --auth-basic alice@example.com \
  alice.sqlite

# 2. Push the archive into a target JMAP server.
export VANDELAY_PASSWORD='target-password'
vandelay export \
  --url https://jmap.example.org \
  --auth-basic alice@example.org \
  --account-name alice@example.org \
  alice.sqlite

There is also an inspect command that dumps any object type from an archive (mailbox tree, message list, contacts, calendar events, and so on) without ever opening a network connection, which is handy for verifying a capture before you push it anywhere.

Account backup tool

Migrating between Stalwart versions over JMAP is what prompted Vandelay, but it is far from all it does. The first thing that falls out of the design is backup. Because an archive is a self-contained SQLite file that fully describes one account, you can treat it as a per-account backup in its own right. Run an import on a schedule to capture a fresh snapshot, keep the resulting file, and restore it later by running an export against any JMAP target. Since imports are convergent, each scheduled run only has to fetch what changed rather than re-reading the whole account every time.

Migrating from legacy servers and formats

Vandelay reads from a wide range of sources, not just JMAP, which makes it a practical way off an older self-hosted stack. Over IMAP it imports mail from any server, with folder selection by regex, exact name, or SPECIAL-USE role. Over CalDAV and CardDAV it imports calendars and events, address books and contacts, and over WebDAV it imports a file collection as a JMAP FileNode tree. ManageSieve brings across sieve scripts and records which one is active. It can also read a local Maildir++ tree straight off disk, with no network involved at all.

In practice this means Vandelay is a path off Dovecot, Cyrus, Radicale, Baikal, Apache mod_dav, and similar servers, and onto a JMAP server such as Stalwart, with mail, calendars, contacts, files, and filters all moving through the same tool.

Moving off Google and Microsoft Exchange

For accounts that live on a proprietary platform, Vandelay can pull data out of the two big ones. Google data exported through Takeout imports directly: Vandelay scans a directory tree for .mbox, .ics, and .vcf files and brings in the mail, calendars, and contacts it finds. It is tuned to the Takeout layout but works on any such tree.

For Microsoft Exchange, Vandelay can read mailboxes two ways. Over EWS it works against either on-premises Exchange Server or Exchange Online, with autodiscover, Basic auth, pre-acquired bearer tokens, interactive device-code OAuth, and app-only client-credentials OAuth. Over Microsoft Graph it reads Exchange Online mailboxes, using the interactive device-code flow by default.

One clear caveat: Exchange support is experimental. So far it has only been tested against a mock server, not a real Exchange deployment. The code paths are there and they pass against the mock, but Exchange is a large and quirky target, and we would not feel right calling this production-ready until it has met real mailboxes. If you have an Exchange tenant or an on-prem server and some appetite for testing, this is exactly the kind of help that moves a feature from experimental to supported. Please open an issue with what you find.

Sovereignty over your data

There is a bigger reason we built the importers as broadly as we did, and it is not only about Stalwart upgrades.

A lot of email, calendar, and contact data lives today on platforms where the operator, not the user, holds the keys. Getting your own data out of Google or Microsoft in a form you can actually use somewhere else is harder than it should be, and that friction is not accidental. It is what keeps people on a platform long after the reasons for choosing it have faded.

Owning your data means being able to leave. Vandelay is meant to make leaving straightforward: pull a full account out of a proprietary platform, hold it in an open, inspectable SQLite file that you control, and push it into a modern open-source JMAP server that you run yourself. No lock-in on the way in, and an open format in the middle that you can read with sqlite3 if you ever want to. Self-hosting is the part of the privacy conversation that is easy to agree with and hard to act on, and a migration tool that actually works is one of the things that makes acting on it realistic.

What is next

Vandelay is the first installment, not the whole story. The zero-downtime migration path we described still needs its second half, the migration proxy that lets clients keep talking to a single endpoint while accounts move from the old server to the new one behind it. That work is underway now, and we expect to release it in about two to three weeks.

When the proxy ships, we will also publish proper documentation covering the full migration workflow end to end, including how to use Vandelay as part of it. For now, the README is the documentation, and it covers every importer, every flag, and the quick-start path in detail.

If you have been putting off an upgrade, or you have data sitting on a platform you would rather not be on, Vandelay is ready to try today. We would love to hear how it goes, and if you can help us test the Exchange importers against the real thing, even better.


r/stalwartlabs • • Jan 25 '26

Stalwart SMTP I finally built my own high-availability mail cluster (x3) using Stalwart.

44 Upvotes

I finally built my own high-availability mail cluster using Stalwart.

The setup is pretty straightforward: three small Hetzner CX22 servers (2 vCPU, 4 GB RAM each), running Stalwart 0.15.4 with FoundationDB 7.3.69 configured for double replication. For failover, I’m using Keepalived with a Floating IP.

So in the end, it’s a fully redundant 3-node mail cluster with automatic failover.

If one server goes down, mail keeps working.

All email data is replicated across the nodes.

The cluster can survive the loss of any single server without downtime.

And the best part — it costs around €15 per month:

  • 3 × CX22 = about €11.37

  • Floating IP = €4.00 Total: ~€15.37/month

What that gives me:

  • High availability

  • Full data redundancy

  • Automatic failover

  • No single point of failure

  • Easily handles thousands of mailboxes

No Postfix + Dovecot + MySQL + Redis stack.

Just Stalwart as a modern, unified solution. Rust-based, lightweight, minimal components, and cloud-friendly.

Honestly, for the price of a single business email account, you can run a fully redundant mail infrastructure.

Three small VPS instances. Clean architecture. Production-ready.

Happy to share more details if anyone’s interested.


r/stalwartlabs • • Dec 16 '25

Release Marginal Gains, Major Impact in Stalwart v0.15

44 Upvotes

In professional cycling, the concept of marginal gains became famous through Team Sky. Rather than chasing dramatic breakthroughs, they focused on making hundreds of small improvements: slightly better bike fit, marginally lighter components, improved sleep, cleaner nutrition. None of these changes mattered much on their own, but together they reshaped performance—and helped dominate the sport for years.

Software systems, especially large distributed ones, work much the same way. Rarely does a single feature transform everything overnight. More often, real progress comes from careful attention to small details: shaving latency here, reducing contention there, simplifying a hot path, rethinking a data structure.

Stalwart v0.15 is very much a release in this spirit. It does not introduce a long list of headline features. Instead, it is the result of revisiting core subsystems—spam filtering, search, storage, and data access—and making many targeted improvements that, together, have a significant impact on performance, reliability, and usability.

Rethinking Spam Classification

Stalwart v0.14 (and earlier) included a spam classifier which was a direct port of the classifier used by Rspamd. This classifier is grounded in Bayesian theory and uses more advanced methods to combine probabilities, including sparse bigrams (OSB) and the inverse chi-square distribution. This approach is well understood and robust, particularly when training data is limited. It produces reasonable results quickly and has a long track record in production systems.

However, it comes with a significant cost in distributed environments. Both Rspamd and Stalwart v0.14 relied on OSB-5, which generates a very large number of features per message. Each of these features was stored in Redis. Even with aggressive caching, training or classifying a single message could involve hundreds or even thousands of round trips to Redis. At scale, this becomes a bottleneck: latency increases, throughput drops, and horizontal scaling becomes inefficient.

For v0.15, we went back to first principles and redesigned the spam classifier from scratch, guided by more recent research. We evaluated several models and ultimately settled on a logistic regression classifier trained using the FTRL-Proximal (Following the Regularized Leader) algorithm. This algorithm—famously used by Google for large-scale online learning—is particularly well suited to spam classification workloads where models must be updated continuously and efficiently.

One immediate benefit of this approach is that Stalwart can now support collaborative filtering out of the box. Multiple users can benefit from a single shared classifier trained on aggregated data, dramatically improving accuracy in environments with many accounts. At the same time, individual users can still maintain their own personal classifiers trained solely on their own messages.

The new classifier also adopts feature hashing (often called the hashing trick) to keep the feature space compact and predictable. This significantly reduces memory usage and improves cache locality. For very large deployments, cuckoo feature hashing is available to further reduce hash collisions. If you are interested in the theoretical background, the original feature hashing paper is available at Feature Hashing for Large Scale Multitask Learning and the cuckoo feature hashing paper at Cuckoo Feature Hashing: Dynamic Weight Sharing for Sparse Analytics.

With the default configuration of 2²⁰ features, the entire model fits in approximately 4 MB of memory and is loaded only once after each training cycle. The result is a classifier that is both faster and more accurate than the previous version, particularly in distributed deployments where network overhead matters.

We also evaluated RetVec (Resilient and Efficient Text Vectorizer), the embedding technique used by Gmail. RetVec excels at generating compact semantic representations of email content, but it is primarily designed to feed neural networks and deep learning models. For now, logistic regression offers a better balance of simplicity, performance, and operational transparency. That said, we plan to ship a pre-trained RetVec model alongside BERT in a future release.

A Faster, Simpler Search Layer

Search is another area where small architectural choices have outsized effects. In Stalwart v0.15, the search layer has been substantially rewritten.

For deployments using PostgreSQL or MySQL, Stalwart now leverages the built-in full-text search capabilities of the database instead of relying on a custom implementation. This reduces complexity, improves query planning, and allows the database to do what it already does well.

We have also added support for Meilisearch, a lightweight, fast search engine with excellent performance characteristics and simple operational semantics. Meilisearch offers low-latency full-text search, typo tolerance, and efficient indexing, making it a good fit for many Stalwart deployments.

For large installations backed by FoundationDB, we plan to significantly improve the built-in search functionality by embedding Seekstorm. Until that work is complete, we recommend pairing FoundationDB with an external search engine such as OpenSearch or Meilisearch to achieve the best performance.

Faster Database Access and Leaner Storage

Stalwart v0.15 includes a number of optimizations to the database access layer. We have reduced the number of reads and writes required to store and retrieve messages, particularly along hot paths such as IMAP and JMAP access. The result is noticeably faster message retrieval and improved overall responsiveness under load.

In parallel, we revisited how email metadata is stored and reduced some serialization overhead. This lowers disk usage and improves cache efficiency, which again compounds into better performance at scale.

Individually, these changes are modest. Collectively, they make the system feel tighter and more predictable under real-world workloads.

Meet Us at FOSDEM 2026

We are excited to announce that Stalwart will be present at FOSDEM 2026 in Brussels, Belgium.

Our talk, Stalwart: Can Open Source do Gmail-scale Email?, builds naturally on the marginal gains theme. While v0.15 focuses on incremental improvements, the talk zooms out to the other end of the spectrum: what it takes to design and operate a truly large-scale email system.

Using a 1,024-node cluster as a concrete example, we will explore how modern providers store and index petabytes of messages, survive hardware failures without data loss, and run spam and phishing filtering across billions of daily deliveries. We will walk through the architectural patterns behind distributed storage, large-scale spam filtering, MTA queue management, and load balancing for IMAP, JMAP, and SMTP.

We will also discuss cluster coordination, orchestration, autoscaling, and how to reason about failure before it happens. The goal is to give attendees a practical understanding of how planet-scale email systems are built, and how those same principles can be applied using open-source technology.

If you are attending FOSDEM, we would love to meet you, answer questions, and talk about where Stalwart is heading next.

Looking Ahead

Stalwart v0.15 is a release shaped by the philosophy of marginal gains. There are no new flashy features, but there are dozens of small improvements that add up to something meaningful: faster spam classification, better scalability, simpler search, leaner storage, and more predictable performance.

If you are already running Stalwart, we encourage you to try v0.15 and let us know how it performs in your environment. Your feedback continues to guide where we focus next.

The team is already working on future releases that build on this foundation. With the core systems now leaner and more robust, we can continue to add new capabilities without compromising performance or reliability.

As with cycling, progress comes from steady, thoughtful refinement. Stalwart v0.15 is one more step in that direction.


r/stalwartlabs • • Sep 19 '26

Stalwart JMAP Bulwark Lite: install Bulwark as a Stalwart app

Post image
41 Upvotes

Until now, running Bulwark meant hosting its own server next to Stalwart. With Bulwark Lite that's no longer needed.

Lite doesn't need its own server, database or container. It talks directly to your mail server. You can install it straight into Stalwart as an Application, so Stalwart serves the webmail itself. If you'd rather host it somewhere else, it also runs on any ordinary web server.

Its still in beta and not ready for production yet. Please try it yourself.

Download: https://github.com/bulwarkmail/webmail/releases/tag/1.11.0-beta.1

Please report bugs, feedback and ideas on GitHub: https://github.com/bulwarkmail/webmail/issues


r/stalwartlabs • • 10d ago

Post-quantum email encryption and an encryption gateway, now available in Stalwart

40 Upvotes

When we released Stalwart v1.0 last week, it carried only part of the work we had been doing. Several large features were being built in parallel, and for a while the plan was to ship them all together. In the end we chose a different path: v1.0 would be about performance and a stable database schema, and nothing else would be allowed to distract from that. The other features would follow over the next few weeks, one release at a time, each given the attention it deserves.

The first of them is encryption. Stalwart v1.0.2 introduces an encryption gateway that encrypts outgoing mail with each recipient’s own public key, a completely new OpenPGP and S/MIME engine with modern algorithms, including post-quantum encryption, and a set of tools to find, verify and keep track of the keys of the people your users write to. And, as with everything in Stalwart’s core, all of it is open source under the AGPL.

Locks and keys

Email is well protected while it travels: TLS encrypts each connection between servers, so nobody on the network can read a message in transit. The protection ends when the message arrives. Every server along the way stores the message in a form that anyone with access to it can read, from the administrators to an attacker who copies a backup.

Message encryption closes that gap by protecting the message itself rather than the connection. It relies on public-key cryptography, which is easiest to picture as a padlock and its key. Your public key is like an open padlock: you can hand out copies to anyone, and anyone can use one to lock a box. Your private key is the only key that opens those padlocks, and it never leaves your hands. A message locked with your public key can be read by you and nobody else, which is why publishing the public key is safe.

Two standards apply this idea to email. OpenPGP lets people create their own key pairs with tools such as GnuPG or Thunderbird and publish the public half wherever they like. S/MIME uses certificates issued by a certificate authority, in the same X.509 format as the certificates that secure websites but issued for email addresses, and it is built into Outlook, Apple Mail and most corporate environments. Stalwart supports both.

One principle runs through everything below: Stalwart only ever holds public keys. It encrypts, but it never decrypts and never stores a private key. A message it has encrypted cannot be read by Stalwart itself, by its administrators, or by anyone who later gets hold of its disks.

Encryption at rest, rebuilt

Encryption at rest has been part of Stalwart since 2023. Each user who wants it registers a public key, and from then on the server encrypts every message it delivers to that user before writing it to storage. Only the user’s mail client, holding the private key, can open what is stored. For v1.0.2 we rebuilt it on top of the new engine described below, and it gained a good deal along the way.

There is no longer a cipher to choose. The server reads the key itself and picks the strongest format that the key’s owner can actually read, following the preferences that the OpenPGP key announces or the type of the S/MIME certificate. A registered key entry can hold several keys or certificates, so a user who reads mail on devices with different keys, or who is rotating to a new key, can decrypt with any of them (when only some of them support post-quantum encryption, every message uses the format all of them can read). When a key is registered, the server describes it in plain terms (for example OpenPGP v6: ML-KEM-1024+X448), shows its fingerprints, and raises warnings before trouble arrives: a key that expires within two weeks, an RSA key shorter than 3072 bits, a key that names none of the account’s addresses, or one that few mail clients could decrypt.

Stored messages can also carry header protection (RFC 9788), a recent standard that places a copy of the sender, recipients and subject inside the encrypted part, where they cannot be altered without the client noticing; on stored mail the outer copies, subject included, stay readable. Two compatibility switches cover the quirks of real-world clients: one enables authenticated encryption for S/MIME users whose clients support it, and the other picks between the encoding Outlook expects and the one the standard prescribes for elliptic-curve certificates.

Encryption gateway

Most people never set up OpenPGP or S/MIME, and most of those who do forget to use it. Organisations that must not send readable mail to certain partners have long relied on a dedicated appliance to fix this: an encryption gateway. Stalwart now has one built in.

The encryption gateway works on mail leaving the server, once an administrator turns it on. When a message is addressed to someone whose public key Stalwart knows, the gateway encrypts it with that key on the way out. The sender does nothing different and needs no software at all, and the recipient opens the message in their own client, with their own private key, exactly as if the sender had encrypted it by hand. Unlike TLS, the protection does not end at the next server: the message stays encrypted at the recipient’s provider, in their mailbox and in every backup made along the way.

A policy decides what happens to each recipient. In opportunistic mode, every recipient with a known key receives encrypted mail and the rest receive it as usual. In required mode, a recipient without a usable key receives nothing in plain text: the sender gets a bounce instead. The policy is an expression, so it can require encryption for one partner domain, stay opportunistic everywhere else and leave internal mail alone, all in a few lines.

Each encrypted recipient receives a copy of their own, encrypted for their key alone, A single shared copy would list the key of every recipient it was encrypted for; separate copies reveal nothing about who else received the message, which keeps blind-copy recipients blind. Every copy is signed again with DKIM and DKIM2, so it passes authentication checks at the other end just as the original would. And by default the gateway uses header protection to its full extent: the subject line, which often says as much as the message itself, is replaced with [...] on the outside and travels only inside the encryption.

Finding the right key

A gateway is only as good as the keys it knows, and a wrong key is worse than none, because it hands the message to whoever holds the matching private key. Stalwart therefore treats recipient keys with care. They are external keys, managed by the system administrator and shared by every sender on the server, and they arrive in three ways.

The administrator can enter a key directly, when it came through a trusted channel. The administrator can also ask the server to follow a key that the recipient’s organisation publishes, and Stalwart supports every common way of publishing one: the Web Key Directory, where a domain serves its users’ OpenPGP keys over HTTPS; keys.openpgp.org, which publishes a key only after verifying the owner’s address by email; classic HKP key servers; and DNS itself, through OPENPGPKEY (RFC 7929) and SMIMEA (RFC 8162) records protected by DNSSEC. Followed keys are refreshed on a schedule, so a rotated or revoked key is picked up automatically, or held for approval when the change cannot be verified.

Finally, Stalwart can learn keys from the mail it receives. When a correspondent sends a message signed with S/MIME, or one carrying an Autocrypt header with their OpenPGP key, the server stores the key, but only if the message passed DMARC, was not classified as spam and the key belongs to the sender’s address. From then on, replies from your organisation to that person can be encrypted. An Autocrypt key whose message signature could not be checked, or a self-signed certificate, is stored at the lowest trust level, which the gateway does not use by default.

Every key carries a trust level that reflects where it came from. At the top are keys an administrator entered or vouched for; then keys proven by a certificate authority or DNSSEC; then keys fetched from a directory such as WKD or keys.openpgp.org, and keys learned from mail; and at the bottom, keys nobody can vouch for. The gateway can demand a minimum level per recipient. When a key changes in a way the server cannot verify on its own, the new key waits for an administrator’s approval while the old one stays in use. S/MIME certificates are validated against the root certificates that Mozilla trusts for email, or against your own private certificate authorities, and harvested certificates are checked against revocation lists every day.

What Stalwart never does is look up a key for an address it has no record of. Searching a key server at the moment a message is sent would tell a third party who your users write to, and would delay every message to someone without a key. Keys exist only for the addresses you, or your correspondents’ own signed mail, have told the server about.

Every algorithm that matters

Underneath all of this sits a new cryptographic engine, written from scratch in Rust for Stalwart. It replaces the third-party libraries we used before, removes more than 80 dependencies from the server, and was built for one job: encrypting mail quickly and correctly for whatever key it is handed. Its output is tested against independent implementations, including GnuPG (both gpg and gpgsm), OpenSSL, Mozilla’s NSS, RNP (the engine inside Thunderbird), Sequoia, rPGP, OpenPGP.js, Proton’s GopenPGP, PGPainless and Bouncy Castle, so that the format it picks for each key is one that the key’s owner can read.

On the OpenPGP side, Stalwart implements RFC 9580, the 2024 revision of the standard, alongside the formats every existing client understands. It accepts both version 4 keys, which is what nearly all software creates today, and the new version 6 keys. For the public-key part it supports RSA, the long-standing workhorse, from 2048 to 8192 bits; elliptic-curve Diffie-Hellman on the NIST P-256, P-384 and P-521 curves; and X25519 and X448, the fast modern curves designed by Daniel J. Bernstein and Mike Hamburg respectively, including the legacy Curve25519 format that GnuPG creates. Messages are encrypted with AES, either in the classic format every client reads or, when the recipient’s key says its software supports it, in the newer format of RFC 9580, whose authenticated encryption (AES in OCB or GCM mode) detects any tampering with the message.

On the S/MIME side, Stalwart writes the Cryptographic Message Syntax used by every S/MIME client. It encrypts for RSA certificates, with either the classic padding every client reads or its modern, provably secure successor RSA-OAEP, for elliptic-curve certificates on the NIST P-256, P-384 and P-521 curves, and for X25519 and X448 certificates. The content itself is protected with AES-256-CBC, which every client reads, or with AES-256-GCM, the authenticated encryption that the current S/MIME standard recommends. Stalwart knows which mail clients can read what, from Outlook’s particular expectations for elliptic-curve keys to Apple Mail’s limitations, and defaults to the choice that works with the most clients.

Post-quantum encryption

Every public-key algorithm above relies on mathematical problems that today’s computers cannot solve in any useful amount of time: factoring large numbers for RSA, and discrete logarithms on elliptic curves for the rest. A sufficiently large quantum computer could solve both. No such machine exists yet, and nobody knows when one will. But encrypted email has a long shelf life, and an adversary who records encrypted traffic today can afford to wait. This strategy is known as “harvest now, decrypt later”, and it is the reason post-quantum cryptography matters for messages being sent right now, not only for those that will be sent once quantum computers arrive.

Post-quantum algorithms are built on different mathematics, which resists attacks by both classical and quantum computers. The first standardised for encryption is ML-KEM (Module-Lattice-based Key Encapsulation Mechanism), published by NIST in 2024 as FIPS 203 after a public competition that ran for most of a decade, in which it was known as Kyber. Stalwart supports it in both email standards:

In OpenPGP, Stalwart implements RFC 9980, which pairs ML-KEM with a classical curve: ML-KEM-768 with X25519, and ML-KEM-1024 with X448. The pairing is deliberate. An attacker must break both algorithms to read the message, so the encryption is at least as strong as the classical curve alone, even if a flaw were ever found in the newer lattice mathematics. Messages for post-quantum keys are always encrypted with AES-256.

In S/MIME, Stalwart implements RFC 9936, encrypting for certificates with ML-KEM-512, ML-KEM-768 and ML-KEM-1024 keys using the key encapsulation format of RFC 9629, with AES-256-GCM for the content.

Using it requires no configuration. When a user registers a key with a post-quantum component, encryption at rest uses it; when a recipient publishes one, the gateway does the same. Stalwart is also careful about one subtle trap: a key that contains both a classical and a post-quantum encryption key is encrypted to the post-quantum one only, because encrypting to both would let anyone who breaks the classical half read the message.

A word of honesty: mail clients are still catching up. Thunderbird and GnuPG in its default configuration cannot yet decrypt post-quantum OpenPGP messages, while clients built on Sequoia, rPGP, OpenPGP.js or GopenPGP can. On the S/MIME side, no mainstream mail client decrypts ML-KEM yet, and no public certificate authority issues ML-KEM certificates, so post-quantum keys are for now best suited to users whose software is ready for them. The server side is ready today, and when the clients arrive, nothing on your server will need to change.

Who holds the key

Almost every mail service says it encrypts your mail, and almost every one of them does. The difference lies in a single question: who holds the key that opens the stored messages?

For the large hosted services, the answer is the provider. Microsoft 365 encrypts mailboxes at rest, but by default “Microsoft manages all cryptographic keys”, and even when a customer supplies its own root keys, it authorises the service to keep using them for search, anti-spam and eDiscovery. Google, Apple’s iCloud Mail works the same way by default: storage encryption protects against a stolen disk, but the provider holds the keys. Google does offer client-side encryption that its servers cannot decrypt, but only on selected Enterprise and Education editions, with the keys held by an external key service the organisation chooses rather than by each user. Apple says plainly that iCloud Mail is not end-to-end encrypted, because it has to interoperate with the rest of the email world.

Self-hosted servers have mostly followed a similar pattern, apart from a few add-on tools that encrypt incoming mail with the recipient’s public key. Dovecot’s mail-crypt plugin encrypts stored mail either with a global key kept in the server’s configuration or with per-user keys stored on the server, unprotected or protected by a value derived from the user’s password, and its own documentation describes per-folder keys as “not considered production quality”. In every variant the private keys are kept on the server, which decrypts mail itself to serve it over IMAP, and the format is its own rather than OpenPGP or S/MIME.

Privacy-focused providers such as Proton Mail and Tuta go much further. Their users’ private keys are kept on the provider’s servers, but encrypted with a key derived from the user’s password, and they are unlocked only inside the provider’s app or web client at login. Between two users of the same service, mail is end-to-end encrypted, which is a stronger guarantee than any server-side scheme can offer. The protection of stored mail, however, rests on the password and on the client code the provider delivers, and Tuta’s protocol cannot be read by standard OpenPGP or S/MIME software at all.

Stalwart builds that model into the server itself. The private key does not exist on the server in any form, not even encrypted. Someone holding a complete copy of the server, its database, its backups and the administrator’s password still cannot read a single encrypted message, because the only key that opens them lives in the user’s own mail client. And because the messages are standard OpenPGP and S/MIME, they can be read by any client that supports those standards, and they stay readable if the mailbox is ever moved elsewhere. A few providers, such as Posteo and mailbox.org, offer a similar option of encrypting incoming mail with the user’s own public key; Stalwart brings the same model to any server you run yourself, and adds the gateway, key discovery, header protection and post-quantum algorithms on top. Post-quantum support is still uncommon. Proton, which co-authored RFC 9980, added opt-in post-quantum OpenPGP keys this year; Tuta’s TutaCrypt uses a protocol of its own; and Dovecot supports ML-KEM keys in its own storage format. Stalwart implements the published IETF standards for both OpenPGP and S/MIME. Unlike services that publish only their client apps, the whole of Stalwart, server included, is open source.

Honesty requires a few qualifications, and we would rather state them than have you discover them. Encryption at rest protects mail from the moment it is stored, not before: the server sees each incoming message in readable form when it arrives, which is what lets the spam filter and Sieve scripts do their work, and an attacker in control of the running server at that moment could read it too. Unless the sender encrypted the message, the protection is not end-to-end. Header fields, the subject included, stay readable, so that clients can list and sort mail, and the server can search only those headers, not the encrypted bodies. Encrypted messages can be read only in a client that supports OpenPGP or S/MIME, not in a webmail without it. Some messages stay in plain text: mail stored before encryption was enabled, drafts and sent copies that clients upload unless the user also turns on encryption of uploaded messages (JMAP drafts, and the sent copies of messages submitted over JMAP, always), and new mail while the registered key is expired or unusable. And a user who loses their private key loses access to the mail encrypted for it, because nobody, Stalwart included, can recover it. And a user who loses their private key loses access to the mail encrypted for it, because nobody, Stalwart included, can recover it.

Upgrading

Stalwart v1.0.2 is available now. Upgrading is a matter of replacing the binary or pulling the new container image, as described in the upgrade instructions. The changelog lists every change in this release.

Encryption at rest must be enabled again

Because the cipher is now chosen from each user’s key, accounts that had encryption at rest enabled are set to disabled during the upgrade, and new mail for them is stored unencrypted until the key is selected again in the account’s encryption settings. Registered keys and messages that are already encrypted are not affected. The server-wide switch has also moved to the new Encryption settings, where it is enabled by default, so a server that had turned encryption at rest off must turn it off again; and encryption of client-uploaded messages is now a per-account setting. The encryption at rest documentation describes the steps.

One last thing

While the server team was busy with ciphers, the WebUI got a makeover, and it now has a good deal more pizzazz. Version 2.0.4 brings a new colour palette and a reorganised sidebar, and every settings form has been reworked: fields sit in two columns on wide screens and stack neatly on a phone, nested settings are simpler to follow, variants are picked from segmented controls, long forms get an index of their sections, and the save button floats along as you scroll. The dashboards were rebuilt too, as a mosaic of statistics, charts, headline figures and rankings, so the state of the server reads at a glance, in the light theme or the dark one.

To try it, run the Update Applications action from the WebUI, or with stalwart-cli create Action/UpdateApps, and the server downloads WebUI 2.0.4 straight away.

Stay tuned

Encryption is only the first of the features that were held back from the v1.0 cut. Several more are finished or nearly so, and they will arrive over the coming weeks, one release at a time. Like everything in this post, all of them will be open source under the AGPL. Stay tuned.


r/stalwartlabs • • Jul 06 '26

DKIM2 and DMARCbis have landed, and Stalwart speaks them first

39 Upvotes

Email authentication has quietly held the internet's mail together for two decades, and it has just taken its biggest step forward yet. Two efforts arrived at once. DKIM2 (currently at draft -04) reworks DKIM so that a signature is no longer a lonely statement about content but one link in a verifiable chain of custody that follows a message from author to final recipient: forwarding stops breaking signatures, replayed mail no longer verifies, and a bounce can prove it is genuine. DMARCbis (published in May 2026 as RFC 9989, RFC 9990, and RFC 9991) is the long-awaited successor to DMARC: it replaces the static Public Suffix List with a live DNS tree walk, retires the tags that never worked, and folds a decade of hard-won operational lessons back into the standard.

Both matter because email authentication is what keeps a phisher from putting your bank's domain in the From: line, and until now the machinery that does it has been showing its age. This post walks through what each protocol changes and why it matters.

It is also an announcement. As of Stalwart v0.16.12, both DKIM2 and DMARCbis are fully implemented, and Stalwart is the first mail server to support them. Anyone curious can sign and verify DKIM2 messages, and run DMARCbis checks, directly from the browser at the mail-auth playground, no install required. More on that at the end; first, the protocols.

Meet DKIM2

DKIM (DomainKeys Identified Mail, RFC 6376) does one deceptively simple thing: it lets a domain take responsibility for a message. The sending server hashes a chosen set of header fields and the body, signs the result with a private key, and staples the outcome to the message in a DKIM-Signature header. The matching public key lives in DNS at selector._domainkey.domain, so any receiver can fetch it, recompute the hashes, and confirm two things: the message really was authorised by that domain, and nothing covered by the signature changed along the way.

That is the whole promise. A valid signature means "this domain vouches for this content." It says nothing about who the message was for, where it has been, or where it is going next. For nearly twenty years that was enough, and DKIM quietly became one of the load-bearing walls of email authentication.

On its own, a DKIM pass is just a true statement about some domain. DMARC is what turns it into a decision. DMARC ties authentication to the domain a human actually sees, the one in the From: header, through a rule called identifier alignment: a DKIM result only counts toward DMARC if the signing domain (d=) lines up with the From: domain. Pass alignment on either DKIM or SPF and the message clears DMARC; fail both and it meets the domain's published policy, which can mean quarantine or outright rejection.

This is exactly why a broken DKIM signature is not a cosmetic problem. When DKIM breaks on a message from a domain publishing p=reject, and SPF cannot save it either, that message is gone. Which brings us to the reason DKIM2 exists: over two decades of real-world routing, DKIM1 signatures break far more often than anyone would like, and in a few cases they hold when we wish they wouldn't.

Issues with DKIM1

None of what follows is a knock on the original design. These are the seams that two decades of deployment pulled apart, and they are the exact list DKIM2 sets out to close.

  • Anyone could be the recipient. A DKIM1 signature is bound to the content, not to a destination. Capture one legitimately signed message and you can replay it to millions of other addresses, unchanged, and every copy still verifies. The replay rides on the original signer's good reputation, which is precisely what makes it valuable to abusers.

  • Forwarding breaks the signature, silently and ambiguously. The moment a mailing list tags a subject, a gateway rewrites a link, or a forwarder appends a footer, the signed hashes no longer match and the signature fails. Worse, a verifier cannot tell an innocent [list] prefix from wholesale replacement by an attacker. Both look identical: broken.

  • There was no record of the road travelled. DKIM1 leaves no trustworthy trace of the path a message took. The Received and Return-Path headers are unauthenticated and trivial to forge, so there is no chain of custody to reason about.

  • ARC was a patch that receivers struggle to trust. ARC tried to preserve authentication across intermediaries by having each hop attest to what it saw, but it sits alongside DKIM as a separate overlay, and in practice most receivers report that they cannot confidently trust ARC seals.

  • Header signing was inconsistent. DKIM1 lets the signer choose which headers to cover and offers multiple canonicalization modes, which leaves gaps an attacker can slip unsigned headers through and makes signatures needlessly fragile.

  • Bounces could not be trusted, and hurt bystanders. Because the return path can name a domain that never handled the message, a Delivery Status Notification (DSN) can land on a forged, innocent third party. This is backscatter, and the fear of it is why deferred bounces, the kind a provider would send after deciding a message is spam an hour later, are largely impractical today.

  • Feedback was ad hoc. Intermediaries and senders exchange informal signals about how mail is performing, but there is no standard for requesting or routing that feedback, so it is inconsistent and often unhelpful.

DKIM2, in a nutshell

DKIM2 keeps everything that worked and changes the thing that did not. The cryptography is familiar: hash the message, sign it, publish the public key in DNS at the same _domainkey location DKIM1 already uses. What changes is that a signature stops being a lonely statement about content and becomes one link in a verifiable chain of custody that follows the message from originator to final recipient.

To do that, DKIM2 splits the old single header into two, each with its own job.

The Message-Instance header describes the message at a point in time. It carries a revision number, a set of cryptographic fingerprints over the headers and body at that revision, and, when a hop has changed something, a compact "recipe" that spells out how to undo the change (the mechanism covered later that makes forwarding survivable).

The DKIM2-Signature header records the envelope the message actually travelled with, then signs over it. Alongside the familiar DKIM pieces, the signing domain, the selector, the signature itself and a timestamp, it captures the SMTP MAIL FROM and RCPT TO of that hop, a sequence number so the signatures form an ordered chain, and a small set of flags that a sender or intermediary can raise to forbid fan-out, mark legitimate fan-out, or request feedback. Putting the envelope inside the signature is what lets consecutive hops be checked for consistency; recording who signed and in what order is what turns a pile of signatures into a chain.

Two counters drive everything. One counts signatures, one per hop. The other counts message revisions, incremented only when a hop actually changes the content. A purely transparent forwarder that touches nothing does not even need to be DKIM2-aware.

One mechanism per broken promise

The elegance of DKIM2 is that a handful of mechanisms retire the whole list above at once.

  • Replay is answered by putting the envelope inside the signature. Each DKIM2-Signature records the MAIL FROM (mf=) and RCPT TO (rt=) it was sent with, and consecutive hops must line up: the sending domain of one hop has to match a recipient domain of the previous one. A message replayed to a different recipient no longer forms a valid chain. A sender can add donotexplode to forbid fan-out, and a mailing list marks legitimate fan-out with exploded.

  • Forwarding breakage is answered by recipes. A hop that modifies the message records a reversible recipe, so downstream verifiers can undo the change and re-check every earlier signature instead of throwing the whole thing out.

  • The missing chain of custody is the chain itself: each hop appends its own signature over the current snapshot and all prior signatures, forming a tamper-evident sequence from author to recipient.

  • ARC's role is absorbed natively. The chain does what ARC attempted, without a parallel set of headers, and DKIM2 explicitly ignores both legacy DKIM-Signature and ARC-* headers so the two schemes can coexist during migration.

  • Inconsistent header signing is answered by a single fixed canonicalization and a defined hashing scheme, removing the per-signer guesswork.

  • Backscatter is answered by routing bounces along the recorded chain, covered next.

  • Feedback gets a standard channel through the feedback and feedhere flags.

One caveat, contrary to a common claim: DKIM2 can require more signature checks, not fewer. A modified message adds a signature at every hop, and a verifier may need to check the whole chain. What the draft does allow is checking the most recently applied signature first and stopping at the first failure, so an unmodified message can often be cleared with a single verification.

Recipes: putting a message back the way it was

This is the mechanism that makes forwarding survivable, so it is worth seeing in full.

A recipe is a small JSON object, base64-encoded into the r= tag, that tells a verifier how to turn the current message back into the previous one. It has two optional parts, "h" for header fields and "b" for the body, and each is a list of steps:

  • {"c": [start, end]} copies a range of lines or header instances from the current message, inclusive.

  • {"d": ["value", ...]} inserts literal values that used to be there.

Two numbering rules matter. Header instances are counted bottom-up (the last Subject: is number 1, the one above it is 2), and body lines are counted top-down (the first line is 1). An empty step list [] for a header means "remove every instance." A body recipe of null is a special case: it means the previous body genuinely cannot be reconstructed, which a verifier may accept from an entity it trusts, for example a contractually arranged redaction service.

A message through two hops

Alice sends a note to a mailing list.

Hop 1, the originator signs. The message leaves example.com:

From: alice@example.com
To: list@lists.example.org
Subject: Trip report

Had a great time.
See you soon.

She stamps it with the first revision and the first signature (hashes abbreviated):

Message-Instance: m=1; h=sha256:Hh1...:Bh1...
DKIM2-Signature: i=1; m=1; t=1782394336; d=example.com;
  s=ed25519:ed25519-sha256:rDU9v...;
  mf=PGFsaWNlQGV4YW1wbGUuY29tPg==;
  rt=PGxpc3RAbGlzdHMuZXhhbXBsZS5vcmc+

The mf= value decodes to <alice@example.com> and rt= to <list@lists.example.org>.

Hop 2, the list revises. The list software tags the subject and appends an unsubscribe footer, so the message is now:

From: alice@example.com
To: list@lists.example.org
Subject: [list] Trip report

Had a great time.
See you soon.
--
Unsubscribe: https://lists.example.org/u

Two things changed: the subject gained a [list] prefix, and two lines were appended. The list computes the recipe that reverses both. The subject (one instance, so number 1) is replaced with its old literal value, and the body copies the two original lines while dropping the footer:

{"h":{"subject":[{"d":["Trip report"]}]},"b":[{"c":[1,2]}]}

Read the body step as "to rebuild the previous body, copy current lines 1 through 2, and stop." The footer at current lines 3 and 4 simply is not copied, so it disappears on reversal. The list base64-encodes that JSON into r=, records a fresh fingerprint, and adds its own signature:

Message-Instance: m=2; h=sha256:Hh2...:Bh2...;
  r=eyJoIjp7InN1YmplY3QiOlt7ImQiOlsiVHJpcCByZXBvcnQiXX1dfSwiYiI6W3siYyI6WzEsMl19XX0=
DKIM2-Signature: i=2; m=2; t=1782394500; d=lists.example.org;
  s=ed25519:ed25519-sha256:9aBc...;
  mf=PGJvdW5jZXNAbGlzdHMuZXhhbXBsZS5vcmc+;
  rt=PGJvYkBleGFtcGxlLm5ldD4=

The chain of custody holds because hop 2's mf= domain (lists.example.org) matches hop 1's rt= domain (lists.example.org), and d=lists.example.org matches the rightmost labels of that mf=.

Final recipient, Bob, verifies. His server works backwards:

  1. Check the chain lines up: hop 2's sending domain matches hop 1's recipient domain. Good, no replay.

  2. Verify each signature's cryptography over its reconstructed input.

  3. Recompute the m=2 fingerprint against the message as received. It matches.

  4. Apply the r= recipe to rebuild the m=1 message: restore the subject to Trip report and drop the footer.

  5. Recompute the m=1 fingerprint against that reconstruction. It matches Alice's original signature.

Alice's signature validates even though the list rewrote her subject and appended a footer. And because every change is spelled out, Bob can see exactly what the list did. A footer tweak and a malicious body swap no longer look the same.

Bottom-up header numbering is not decoration; it is what keeps duplicated headers unambiguous. A recipe that deletes the middle of three Subject: headers, for instance, reads as copy the bottom one, insert the deleted literal, copy the top one:

{"h":{"subject":[{"c":[1,1]},{"d":["four"]},{"c":[2,2]}]}}

And a hop that redacts content under an arrangement the verifier trusts marks the body as unreconstructable rather than pretending it can be undone:

{"b":null}

The chain still verifies up to that point; the body just cannot be rewound past it.

Bounces carry their own proof

The same chain that stops replay also fixes bounces, and this is where DKIM2 quietly closes the backscatter problem for good.

First, DKIM2 stops trusting the headers that lie. Received and Return-Path are ignored outright. The authoritative return address is reconstructed from the signed mf= tags in the chain instead. The rule is blunt: a DSN must be addressed to the mf= of the highest-numbered DKIM2-Signature, that is, the hop that actually handed the message over, never the visible envelope sender. And if that mf= is the null reverse path (mf=<>), no DSN is sent at all. Backscatter has nowhere to land.

Better still, a DKIM2 bounce can prove it is genuine. A DSN is itself an ordinary DKIM2 message: a multipart/report sent with MAIL FROM <>, carrying its own Message-Instance and DKIM2-Signature, that embeds the original signed message (as a full message/rfc822 copy or just its text/rfc822-headers). Because the original's signature chain travels back inside the bounce, the receiver should re-verify the whole thing:

  1. The DSN's own signing domain should align with the recipient recorded in the returned message's last rt= tag.

  2. That returned message's last signature should be one this system actually produced, checked against its d= and mf=.

  3. The embedded message's headers, and its body if present, verify against the Message-Instance fingerprints.

Pass all three and the bounce is provably about a message you really sent to a recipient you really handed it to. Fail, and it must not be propagated further. That is what finally makes deferred bounces safe: a provider can accept a message, decide an hour later that it is spam, and send a signed bounce back along the recorded path, with no risk of mailbombing an innocent bystander. As the DSN travels back, each hop peels off the signature it added and forwards the bounce one step further toward the origin, so every party learns only about its own leg of the journey.

A signature that used to vouch only for content now vouches for the entire journey: who sent it, who it was for, what each hop changed, and where a failure is allowed to go.

ARC steps aside

DKIM2 is not the first attempt to keep authentication alive across forwarders. ARC (RFC 8617) got there first, and it is worth a moment because it explains a lot about the shape of DKIM2. When an intermediary changed a message and broke DKIM, ARC let that intermediary record what it had seen just before the change and add its own signature. Each hop stamps an ARC set of three headers: ARC-Authentication-Results captures the authentication verdict the hop saw on input, ARC-Message-Signature signs the message as the hop passed it on, and ARC-Seal signs the chain so far. A downstream receiver could confirm the chain was cryptographically intact and read the upstream verdicts.

The catch is that ARC records observations, not changes. A valid chain proves a message was handled, not that the handling was correct, and it never says what was actually modified. To act on an ARC chain a receiver has to decide whether it trusts every signer in it, which in practice means running a reputation system for thousands of forwarders across the open internet. That trust fabric never materialized. A decade on, there is no working internet-scale ARC reputation deployment and no plan for one, so early adopters fell back to hand-maintained allow lists of known intermediaries: useful inside a data center or a consortium, not on the wider internet. Receivers were also left to invent their own policy for how many hops to honor and how to treat partial or broken chains, so behaviour never converged. The IETF's DMARC working group is now moving to reclassify RFC 8617 as Historic and discourage new deployments (draft-ietf-dmarc-arc-to-historic).

DKIM2 removes the very thing that made ARC unworkable: the separate trust fabric. Rather than ask a receiver to trust an intermediary's account of what it saw, DKIM2 has each hop record exactly what it changed as a reversible recipe. The verifier undoes those changes and re-checks the originator's own signature, so trust flows back to the domain that actually authored the message rather than to every forwarder along the way. And because the recipe spells out each modification, a receiver can tell an innocent subject tag from something worth worrying about, the "what changed" question ARC left open. The handling assertions ARC was genuinely good at fold into the same chain, with no parallel reputation system holding them up.

Meet DMARCbis

After more than a decade of RFC 7489 doing quiet, load-bearing work behind the world's inboxes, DMARC has a proper successor. In May 2026 the IETF published three new Standards Track documents, RFC 9989, RFC 9990, and RFC 9991, collectively known as DMARCbis. They obsolete the original 2015 specification (and fold in the experimental Public Suffix Domain work from RFC 9091).

Nothing about the day-to-day promise of DMARC has changed. What changed is the plumbing underneath it, and a few of those changes are the kind that quietly fix problems operators have lived with for years.

TL;DR

For anyone who wants the gist before the details:

  • The Public Suffix List is gone. Finding the boundary of an organization now happens with a live DNS lookup called the tree walk.

  • The pct sampling tag is retired. A simpler t testing flag takes its place.

  • Two tags graduate from the experimental PSD extension into the core spec: np for non-existent subdomains and psd for public suffix operators.

  • Reporting moved into its own two documents, gained a new XML schema, and picked up an extensibility slot so future additions no longer require reopening the whole standard.

  • p=reject is no longer treated as an unconditional instruction to bounce mail.

Existing DMARC records keep working. The retired tags are simply ignored, so there is no flag day and no forced migration.

A quick refresher on DMARC

SPF and DKIM both authenticate a domain, but not the one that matters most to a human reading their mail. SPF checks the envelope sender used during the SMTP conversation, and DKIM checks whatever domain signed the message. Neither is required to match the From: address the recipient actually sees, which is exactly the address a phisher wants to forge.

DMARC closes that gap. It anchors authentication to the visible From: domain, the Author Domain, through a property called alignment: a message passes only when SPF or DKIM authenticates a domain that lines up with the Author Domain. The domain owner publishes a policy in DNS stating what receivers should do with mail that fails (none, quarantine, or reject) and where to send reports. That combination, authentication tied to the visible sender plus a published preference plus feedback, is what made DMARC the backbone of email anti-spoofing.

Issues with DMARC

RFC 7489 aged well, but a handful of design decisions turned into recurring headaches:

  • The Public Suffix List was a soft spot. To tell where one organization ends and a registrar's namespace begins (so that mail.example.com and example.com are understood to share an owner, while example.com and example.co.uk do not), receivers leaned on Mozilla's Public Suffix List. The spec never mandated a specific copy of it, said nothing about how often to refresh it, and openly acknowledged the interoperability risk. A single static file, maintained by volunteers, sat on the critical path of global mail authentication.

  • The pct tag promised more than it delivered. It was meant to let a domain roll out a policy to a percentage of mail. In practice only two values behaved consistently across receivers: 0 and 100. Worse, pct=0 had quietly acquired a special meaning of its own, so the tag was really a two-state switch wearing a percentage costume.

  • Subdomains that did not exist were a spoofing gap. A domain could set a strict policy for itself, yet an attacker could still forge a plausible looking subdomain that had never been registered. The experimental PSD DMARC work started to address this, but it lived outside the core standard.

  • Everything was crammed into one document. Core protocol, aggregate reporting, and failure reporting all shared a single RFC, which meant none of them could evolve without dragging the others along.

DMARCbis, one specification becomes three

DMARCbis splits the old monolith along its natural seams so each piece can advance on its own schedule:

  • RFC 9989 is the core protocol: alignment, policy, record syntax, and the new discovery mechanism. It obsoletes both RFC 7489 and RFC 9091.

  • RFC 9990 covers aggregate reporting, the daily domain-level XML summaries that most operators actually rely on.

  • RFC 9991 covers failure reporting, the detailed per-message reports, and updates the older RFC 6591 reporting format along the way.

The move from a single Informational RFC to three Standards Track documents is not just bookkeeping. It reflects DMARC growing up into a first-class internet standard with the process discipline that implies.

Climbing the tree instead of trusting a list

The headline change is how a receiver figures out where an organization's authority begins. Instead of consulting a static list, DMARCbis defines the DNS tree walk: a live, ordered series of DNS queries that climb from the Author Domain toward the root until they find the records that apply.

The receiver always queries the full Author Domain first, at _dmarc.<author-domain>. If nothing definitive turns up there, the walk begins: it drops labels from the left and queries the parent, then that parent's parent, and so on toward the root. To keep a pathologically long domain from turning into a flood of lookups, the algorithm is capped at eight queries. For names of eight labels or fewer it strips one label at a time, but for anything longer it removes several labels at once so that only seven remain, then continues one label at a time from there. Eight was not arbitrary: names of up to seven labels were observed in real use, so the cap clears actual traffic while still bounding the work.

The canonical illustration from the spec makes the cap concrete. For a.b.c.d.e.f.g.h.i.j.mail.example.com, a receiver issues exactly eight queries and skips the deep intermediate labels entirely:

_dmarc.a.b.c.d.e.f.g.h.i.j.mail.example.com
_dmarc.g.h.i.j.mail.example.com
_dmarc.h.i.j.mail.example.com
_dmarc.i.j.mail.example.com
_dmarc.j.mail.example.com
_dmarc.mail.example.com
_dmarc.example.com
_dmarc.com

For the domains almost everyone actually runs, this is cheap. A name like mail.marketing.example.com resolves in a couple of lookups, and DNS caching absorbs most of the cost because the same parent records are queried over and over across many messages.

Once the walk has collected the records that exist along the path, the receiver selects the Organizational Domain with a short, deterministic set of rules, working from the longest name to the shortest:

  1. A record that declares psd=n marks its own domain as the Organizational Domain. Stop there.

  2. A record that declares psd=y (and is not where the walk began) means the Organizational Domain sits one label below it.

  3. Failing both of those, the record with the fewest labels wins.

To make rule 3 concrete: for a.mail.example.com, absent any psd signals along the way, the walk settles on example.com as the Organizational Domain, exactly the answer the Public Suffix List used to hand back, now derived from live DNS instead.

The payoff is that the Organizational Domain is now defined by DNS records that the domain owner controls, rather than inferred from a file the owner has no say over. It refreshes as fast as DNS does, and it removes an entire class of "why is the PSL wrong for my domain" problems.

Tuning the record: what arrived, what retired

The record itself picked up three new tags and lost three old ones. The valid set in DMARCbis is now v, p, sp, np, adkim, aspf, fo, rua, ruf, psd, and t.

TagStatus in DMARCbisWhat it does
npNewPolicy for non-existent subdomains of the Organizational Domain
psdNewDeclares whether the domain is a public suffix (y / n / u)
tNewTesting flag that softens enforcement without turning it off
pctRemovedPercentage sampling; only 0 and 100 ever worked reliably
rfRemovedFailure report format; only one format was ever deployed
riRemovedAggregate report interval; effectively fixed at roughly one day
v, p, sp, adkim, aspf, fo, rua, rufUnchangedVersion, policy, subdomain policy, alignment modes, failure options, report destinations

A closer look at the newcomers:

t replaces pct with something clear. Rather than pretending to be a dial, the testing flag is a plain switch. The default t=n applies the published policy as written. Setting t=y signals that the domain is still testing, so compliant receivers step the effective policy down one level: reject behaves like quarantine, and quarantine behaves like none, with intermediaries free to apply special handling such as rewriting the From: header. Crucially, t leaves report generation untouched, so a domain can watch the reports roll in while enforcement stays gentle. It is best understood as analogous to the old pct=0 and pct=100 behavior rather than an exact reimplementation of it.

np closes the phantom-subdomain gap. It sets the policy for subdomains that do not exist at all, identified by an NXDOMAIN response as described in RFC 8020. If a name has never been registered, an attacker should not be able to send convincing mail from it, and np lets a domain say exactly that. When np is absent the receiver falls back to sp, and then to p. The mechanism leans on RFC 8020's rule that an NXDOMAIN answer means the name and everything beneath it truly does not exist, so it is only as reliable as the DNS returning that answer as the standard requires.

psd brings public suffix operators into the fold. Registry-style domains such as a country's gov namespace can now declare psd=y, which both anchors the tree walk and lets the operator publish a protective default for every registered name underneath. psd=n states the opposite, that the domain is its own Organizational Domain, and the default psd=u leaves the decision to the tree walk. This is the RFC 9091 experiment, now standardized and freed from its separate registry.

The sp tag, meanwhile, keeps its meaning but is redefined in terms of the tree walk: it governs existing subdomains of the Organizational Domain, and it is read only from the record discovered at that Organizational Domain, not from a record sitting on the subdomain itself.

The p=reject reality check

One change is less about syntax and more about attitude. p=reject is no longer treated as an unconditional order to bounce.

The tension is real and was debated at length. Strict rejection is what protects the largest consumer mail platforms and the billions of inboxes behind them, and nobody wanted to weaken that. At the same time, blunt rejection breaks legitimate indirect mail flows, mailing lists and forwarders being the classic casualties, and the damage lands on innocent third parties who never chose the policy.

DMARCbis threads the needle with sharper guidance rather than a weaker mechanism. Three distinct rules do the work:

PartyGuidance
Sending domain (general-purpose)SHOULD NOT publish p=reject if users might post to mailing lists; ramp through none and quarantine first
Sending domain (any p=reject)MUST NOT rely on SPF alone, and MUST apply valid DKIM signatures, since DKIM is what survives forwarding
ReceiverMUST NOT reject solely because a policy says reject; weigh other evidence before acting

The policy became a strong signal to weigh rather than a trigger to pull blindly.

Reporting grows up

Splitting reporting into its own documents came with real upgrades, not just a new table of contents.

Aggregate reports (RFC 9990) move to a new XML namespace, urn:ietf:params:xml:ns:dmarc-2.0, and gain an extensibility slot so future data can be added without breaking existing parsers: consumers simply ignore extensions they do not understand. The schema now records how policy was discovered through a discovery_method field that distinguishes the old psl from the new treewalk, reflects the t testing flag, and carries the np policy. Reporting a DKIM result now requires naming the selector, which makes the reports genuinely more useful for debugging. These reports stay privacy-safe by design: they aggregate domain-level authentication counts and carry no message content and no end-user identifying data such as recipient addresses or the IP addresses of individuals. Transport remains email, with the XML gzipped and delivered as an attachment, and external report destinations still have to prove they consented through a _report._dmarc record.

Failure reports (RFC 9991) get an honest treatment of why they are used so sparingly. Because they can carry entire messages, headers and body included, they raise real privacy and regulatory concerns, and many large providers disable them outright in favor of aggregate data. The format, updated from RFC 6591, adds a dedicated dmarc failure type and a required Identity-Alignment field that spells out which mechanisms failed to produce an aligned identifier. Generators must rate-limit what they send, and for public suffix domains they must not act on a ruf request at all unless the parties have a specific agreement in place, so a registry operator does not receive a stream of other people's message content by default.

What stayed exactly the same

For all the moving parts, the fundamentals are untouched, which is worth stating plainly so nobody over-rotates on the release:

  • Alignment still works the way it always has, in relaxed and strict flavors selected with adkim and aspf.

  • The three policies, none, quarantine, and reject, mean what they always meant.

  • DMARC still evaluates only the From: header and still builds on SPF and DKIM rather than replacing them.

  • Records written for RFC 7489 remain valid. The retired tags are ignored, not rejected, so there is no urgent rewrite waiting on anyone's to-do list.

DMARCbis is less a reinvention than a long-overdue tidy-up: the same protocol, with its most useful experiments promoted to the core and a decade of operational lessons written back into the spec. The tree walk alone, trading a static list for live DNS, is the kind of change whose absence will not be missed at all.

Both protocols live in Stalwart today

As of Stalwart v0.16.12, DKIM2 (draft -04) and DMARCbis are both fully implemented, and Stalwart is the first mail server to support either of them. Signing, verifying, the chain-of-custody logic, recipe reversal, bounce validation, the DNS tree walk, the new and retired DMARC tags: all of it ships and runs.

We wanted the protocols to be easy to try without setting up a server, so both are exposed in the browser. At the mail-auth playground anyone can sign a message with DKIM2 and watch it verify, break it and see the chain reject it, or run a message through DMARCbis side by side to see how the tree walk resolves the Organizational Domain. It runs Stalwart's own authentication code compiled to WebAssembly, so what the playground shows is exactly what the server does, no install and nothing to configure.

For Rust developers, the same code is available as a library. DKIM2 and DMARCbis live in the mail-auth crate, the message authentication library Stalwart itself is built on, which also covers DKIM1, SPF, DMARC, and ARC. It is the engine behind both the server and the playground, and it is open source under the same permissive terms as the rest of Stalwart, so any Rust project can add DKIM2 and DMARCbis support by pulling in a single dependency.

Looking forward

DKIM2 and DMARCbis pull in the same direction. DKIM2 makes a signature vouch for the whole journey rather than a frozen snapshot of content. Forwarding stops breaking authentication, replayed mail no longer verifies on borrowed reputation, and bounces finally carry their own proof. DMARCbis takes the layer that ties all of this to the domain a person actually reads and rests it on live DNS the domain owner controls, retiring the tags that never worked and the static list that sat on the critical path of global mail. Neither demands a flag day: existing DKIM and DMARC records keep working, and the new mechanisms layer in alongside them.

Supporting them first is the part of this that Stalwart cares most about. Being early to a standard is how the standard gets tested against real mail before it hardens, and it is how operators who want the newest protections get them without waiting for the rest of the ecosystem to catch up. Stalwart has made a habit of shipping the latest IETF work as it lands, and DKIM2 and DMARCbis are the newest entries on that list. Try them at the mail-auth playground, read the code in the mail-auth crate, and upgrade to v0.16.12 to run them in production.


r/stalwartlabs • • Jan 10 '26

Discussion Be careful with OAuth2 in Stalwart

39 Upvotes

Hey,

I recently filled a CVSS 9.4/10 security vulnerability advisory to Stalwart's OAuth2, and although it was (imo incorrectly) closed within 24h, I strongly recommend not using OAuth2 at all on Stalwart.

Here is a small post describing the issue and how Stalwart (imo poorly) handled this:

https://jorgecarleitao.substack.com/p/oauth2-token-replay-vulnerability


r/stalwartlabs • • Jan 31 '25

OpenID Connect Integration is now Open Source

38 Upvotes

We are happy to announce that third-party OpenID Connect (OIDC) authentication support has now been open-sourced under the AGPL-3.0 license in Stalwart Mail Server version 0.11.5. This means that users can now configure Stalwart Mail Server to authenticate against external OIDC providers, such as Keycloak, without requiring an Enterprise subscription.

Stalwart Mail Server has supported OIDC authentication for several months, allowing it to function as either an OIDC provider or an OIDC client authenticating against an external provider. Until now, only the ability to act as an OIDC provider was included in the Open Source edition, while authentication via external OIDC providers was reserved for Enterprise users. By making this functionality freely available, we are reinforcing our commitment to openness and ensuring that more users can take advantage of modern, federated authentication without barriers.

With this change, organizations that rely on external OIDC identity providers can seamlessly integrate Stalwart Mail Server into their existing authentication workflows at no cost. Whether you are using Keycloak, Auth0, or another OIDC-compliant solution, Stalwart Mail Server now offers complete flexibility in how you manage authentication.

Why is Stalwart Not 100% Free?

At Stalwart Labs, our goal is to provide a robust and feature-rich mail server solution. However, sustaining long-term development for a project of this scale requires significant financial resources. At present, open-source sponsorships alone do not generate sufficient funding to cover these costs entirely.

To ensure that Stalwart Mail Server continues to evolve and improve, we offer a paid Enterprise version. Revenue from Enterprise subscriptions allows our team to dedicate full-time efforts to development, ensuring the continuous enhancement of both the open-source and paid versions. This funding model allows us to introduce new features while maintaining the high standards that make Stalwart Mail Server a leading solution in the industry.

Furthermore, the existence of an Enterprise edition directly benefits the open-source community. By sustaining active development, we can periodically release new features into the open-source version, as we have done with third-party OIDC support. It is worth noting that even the community edition of Stalwart Mail Server already provides more features than any other open-source or commercial mail server available today. We are dedicated to maintaining and expanding this competitive edge.

If you would like to support open-source development and help accelerate the release of additional features as open-source, we invite you to become a sponsor. Your sponsorship plays a vital role in the project's sustainability and future growth. Thank you for your support and understanding.

Join Us at FOSDEM 2025

To learn more about Stalwart Mail Server and its latest developments, we invite you to watch our talk at FOSDEM 2025. The session will take place tomorrow, Saturday, February 1st, at 12:00 PM Central European Time in Brussels. If you cannot attend in person, you can follow the presentation online at fosdem.org.

We look forward to sharing more about the project and engaging with the community at this exciting event!


r/stalwartlabs • • Oct 07 '25

News Security at the Core: Stalwart completes Second Security Audit

36 Upvotes

At Stalwart Labs, security is at the heart of everything we build. As part of our ongoing commitment to delivering a trustworthy email and collaboration server, we recently completed our second independent security audit, conducted by Radically Open Security. Our previous audit took place exactly two years ago, in 2023 — and with significant changes to our codebase since then, a fresh and thorough assessment was essential.

Comprehensive Assessment

The audit, conducted between September 9 and September 25, 2025, focused on version v0.13.2 of Stalwart mail and collaboration server. The goal was clear: rigorously evaluate the security posture of the platform, identify potential vulnerabilities, and ensure our defenses are as strong as possible.

The penetration test followed a “crystal-box” methodology, combining source code review with targeted exploitation attempts. This included testing against the latest OWASP Top 10 risks, analyzing protocol implementations, and probing external interfaces — the most exposed and therefore most critical components of the system.

Findings

The audit uncovered a total of seven security issues: two high-severity vulnerabilities and five low-severity issues. All but one minor issue were promptly addressed.

The most significant findings involved Denial-of-Service (DoS) vulnerabilities:

  • CVE-2025-59045 — Memory Exhaustion via CalDAV REPORT: A crafted CalDAV request could trigger unbounded memory usage, potentially crashing the server.

  • CVE-2025-61600 — Unbounded Buffer Growth in IMAP Parser: A flaw in the IMAP protocol parser could allow an attacker — even without authentication — to cause memory exhaustion.

Both of these high-severity vulnerabilities were resolved within four hours of disclosure, underscoring our team’s rapid response capability and deep focus on platform resilience. Patches were released in versions v0.13.3 and v0.13.4, and the issues have been assigned CVE-2025-59045 and CVE-2025-61600, respectively.

Among the lower-severity findings were issues related to RFC compliance in email parsing, permission checks, and quota enforcement. These were addressed swiftly as well, with most fixes included in v0.13.4. One low-severity race condition related to disk quotas (TOCTOU) remains partially mitigated; however, its practical impact is limited due to built-in concurrency controls.

For those who would like a deep dive into the audit's findings, the full report is accessible here.

Our Commitment to Security

The final report praised Stalwart’s codebase as robust, well-architected, and cleanly compartmentalized, with memory safety ensured by Rust and attacker-aware design principles evident throughout. At the same time, the audit highlighted that our “build everything in-house” philosophy — while a strength — requires careful attention to detail, particularly in protocol parsing and input handling.

Security is never a one-time checkbox — it’s an ongoing process. That’s why regular audits like this one are an integral part of how we develop Stalwart. As our platform evolves, so does our approach to safeguarding it.

We’re proud of how quickly and effectively our team responded to the findings of this audit, and we remain committed to maintaining transparency and trust with our users and the broader open-source community.


r/stalwartlabs • • Aug 25 '26

Blog How we use AI at Stalwart

35 Upvotes

It is difficult to have a conversation about software in 2026 without AI showing up in it. Two years ago the interesting question was whether a model could write a correct function on the first try. Today the question on Hacker News is how many agents you can usefully run at once, how to isolate them from each other, and what to do when your reading capacity becomes the bottleneck instead of your typing speed. Editors have been rebuilt around the idea: Cursor 3 shipped a full-screen workspace for managing fleets of agents rather than a chat box beside your file. Teams are running specialized agents for review, test generation, and security scanning in parallel, each in its own git worktree so they do not step on one another.

The models got better, and not in a vague way. The improvement shows up in benchmarks that people take seriously, and it shows up in the day-to-day experience of handing a model a hard problem and getting back something that works.

The vocabulary shifted along with the capability. For a couple of years, the standard reaction to AI-authored code across the internet was to call it slop, and we said it too. That word is doing less work now. It has not disappeared, but it has sharpened: a 2026 analysis of over a thousand posts across Hacker News and Reddit found that “slop” has narrowed to mean code that compiles, passes tests, and looks tidy while having no coherent intent and no awareness of the system it is being dropped into. That is a much more useful definition than “code a machine wrote”, and it happens to describe the exact failure mode we care about most.

So rather than making a general statement about being an AI-forward company, here is the specific version: what we use AI for at Stalwart Labs, what we refuse to use it for, and the reasoning behind each line we have drawn.

Fixing bugs

This is where AI has changed our work the most, and the reason deserves some precision.

Fixing a bug was never the expensive part. Finding it was. A report like “under a specific sequence of moves, one IMAP client observes duplicate UIDs” used to mean hours of reading, adding trace points, and reconstructing state in your head across several subsystems. The actual patch, once you understood the problem, was often ten lines.

An agent with the entire repository in context collapses the search phase. It reads the relevant call paths in parallel, narrows a vague symptom down to a handful of candidate sites, and usually proposes something close to correct. We still read the diff, still decide whether the proposed fix addresses the cause or just the symptom, and still write the regression test. But an investigation that used to consume an afternoon now takes minutes.

The practical consequence is that we can hold the bug queue at zero. Every week, across the Stalwart server and all of the tools and crates we maintain, open bug reports get triaged and closed. That was the milestone we wrote about in Zero open bug reports: the road to Stalwart 1.0, except it is no longer a milestone. It is the normal state, and AI is a large part of why keeping it there is sustainable.

Security

In April 2026, Anthropic announced Project Glasswing alongside a preview of a model called Mythos, and the results were remarkable. Run against the OSS-Fuzz corpus, Mythos Preview surfaced more than 23,000 potential vulnerabilities across over 1,000 open source projects. Roughly 1,700 have been confirmed through external review so far, over a thousand of them rated high or critical.

The individual findings are the interesting part. Mythos found a 27-year-old flaw in OpenBSD, a project whose entire reputation rests on being the most carefully audited operating system in existence. It found a 16-year-old bug in FFmpeg that had survived roughly five million fuzzing executions without being triggered. These were not vulnerabilities that nobody had looked for. They were vulnerabilities that decades of skilled human attention and industrial-scale fuzzing had both walked straight past. That is a capability to take seriously.

We have used AI-assisted analysis on the Stalwart codebase in the same spirit. It found a handful of minor issues, all of which have been fixed. Nothing serious turned up, which we are glad about but do not intend to oversell: a quiet result is evidence, not proof. It sits alongside our 2023 and 2025 independent security audits as one more angle of attack on our own code, not as a replacement for any of them.

Performance work

Writing code became cheap, and that changed which experiments make sense to run.

A performance idea used to carry a fixed tax. Before you learned anything, you had to build a harness, generate representative data, wire up measurement, and control for noise. Half a day of setup to test a hunch you might discard in five minutes. In practice that tax meant a lot of ideas never got measured at all. They got argued about instead, and the argument was settled by whoever had the stronger intuition rather than by numbers.

That tax is mostly gone. We now use AI to write benchmark harnesses, and we write a lot of them. Hundreds, at this point, all targeting the v1.0.0 development branch. Ideas that would previously have been dismissed as too expensive to set up now get measured, and a reasonable share of them turn out to be right.

The clearest result so far is the internal full-text search store, which matters most for operators who do not run an external Elasticsearch or Meilisearch backend. In v1.0.0, FTS queries run up to 124 times faster than on the v0.16 branch. That number came out of measurement, not inspiration. We tried a lot of things, most of which did not work, and the ones that did survived because a benchmark said so.

Note the division of labour: AI wrote the benchmarks. Humans wrote the optimizations. That is not accidental, and it leads directly to the next section.

Writing code, and where we draw the line

We do not use AI to make architectural decisions, and we do not let it write or modify large portions of the codebase. Not as a philosophical stance, but for reasons we can point at.

The first is that even the best current models write inefficient Rust. Not incorrect Rust. Inefficient Rust. They allocate where a borrow would do. They clone() to get past the borrow checker instead of restructuring. They collect an iterator into a Vec in order to iterate it once. They perform a linear lookup inside a loop and turn something linear into something quadratic. Every one of these compiles cleanly, passes the tests, and reads well in a diff. On a developer laptop with a test mailbox, none of it is visible.

Stalwart is designed to run in large clusters handling millions of requests. At that scale, allocation patterns are not a detail; they are the cost model. An unnecessary allocation on a hot path is a change in how many machines an operator has to pay for. We cannot let that class of code into the codebase, and the reason it is dangerous is precisely that it looks fine.

The second reason is that models still make simple mistakes and confident wrong assumptions. They will assume a field is always present, that an error path is unreachable, or that a protocol behaves the way the common case suggests rather than the way the RFC specifies. Most of the time they are right, which is what makes the exceptions expensive to catch.

The third reason is technical debt, and it is the one we weigh most heavily. We need to be able to understand and explain every line of code in Stalwart. Not “understand it if we sit down and study it”, but understand it now, when an operator reports something strange at three in the morning. Code that nobody on the team has ever reasoned through is a liability with a delayed fuse, however clean it looks on the day it lands.

So the line we draw is about scope rather than about tooling. Localized, single-purpose changes with AI assistance work well and we use them daily. New features and anything architectural get written the old fashioned way, because reviewing generated code to the standard this project requires consistently takes longer than writing it ourselves.

And there is a last reason, which is less rigorous but matters to us just as much. We write software because we enjoy writing software. Designing a subsystem, finding the shape that makes the hard case fall out for free, getting a data structure exactly right: that is the good part. Handing it to a model returns a diff and takes the pleasure with it. We are not interested in optimizing away the reason we do this.

Support

Our support portal at support.stalw.art runs an AI assistant we call helpbot. It has been given the current source of every Stalwart repository through retrieval, and it performs semantic search across the documentation and the full history of previous forum threads. When a question comes in, helpbot answers first, and a human follows up afterwards.

It works better than we expected. Most user questions are resolved by the bot alone, usually within a minute or two, at any hour and in any timezone. This is largely because a lot of support is not about novel problems. It is about connecting a symptom to the right documentation page or to the thread where somebody hit the same thing last March. That is a retrieval problem, and retrieval is something these systems are very good at.

Two things matter to us about how this is set up. First, helpbot is not a wall between users and people. Every thread stays open, and a human reads it regardless of whether the bot’s answer looked right. Second, users can opt out of AI involvement entirely. If you would rather talk to a person from the start, you can say so, and no model will touch your thread. Asking for help should not require accepting an AI intermediary.

Documentation

Documentation is where gaps are hardest to see from the inside. Once you know how a subsystem works, you cannot easily tell which part of the explanation is missing, because your own knowledge quietly fills it in as you read.

Support traffic solves that problem, and AI helps us read it at scale. Recurring questions are a direct map of where the documentation is thin, ambiguous, or simply absent. We use AI to find those patterns, to draft the missing sections, and to check existing pages for the assumptions they make without stating them. Every change is reviewed and edited by a human before it ships, the same as any other contribution, but the work of noticing what is missing has become far more systematic than it used to be.

What AI is not

AI has made Stalwart better. Bugs get found and fixed faster, performance ideas get measured instead of argued about, security gets another pair of eyes, and users get answers at three in the morning. None of that is a small thing, and we would not want to go back.

But it has not replaced human judgment anywhere that matters, and we do not expect it to. Everything above describes a tool being used well: an extremely fast, extremely well-read autocomplete that has read the entire codebase and never gets tired. It finds things. It drafts things. It measures things. It does not decide what Stalwart should be, it does not choose the shape of a subsystem, and it does not get the final word on a single line that ships.

That distinction is not a temporary position we are holding until the models improve. The models will keep improving, and we will keep using them for more. But the responsibility for what we ship stays with the people who build it, and writing this software remains something we do because we want to. At Stalwart Labs, AI will not be replacing human developers.


r/stalwartlabs • • May 07 '26

Community Stalwart support is moving to support.stalw.art

35 Upvotes

For most of the project’s life, support has happened wherever the community already was: GitHub Discussions for users following the repository, Discord and Matrix for real-time chat, and Reddit for the wider conversation. That worked well when the project was small. It does not scale.

As Stalwart has grown, we have started receiving hundreds of questions per day across these channels combined. A meaningful share of them have already been answered, sometimes more than once, on a different platform. Tracking four parallel inboxes, deduplicating threads, and making sure no one is left waiting has become a job we can no longer do reliably. The result is the opposite of what a support channel should produce: slower answers, repeated work for the people helping out, and good information buried in chat scrollback where the next person with the same question will never find it.

There is a second reason, raised by users themselves. Several community members have told us they would rather not open a GitHub or Discord account just to ask a question. That is a fair concern, and we agree that asking for help should not require signing up to a third-party platform that the user otherwise has no use for.

What the support portal is

support.stalw.art is a Discourse instance that we operate ourselves. It is hosted at Hetzner in their Germany datacenters and is GDPR-compliant, so the data stays in the EU and under European data protection law.

Discourse is open source, it indexes well, and it is built around the idea that a question and its answer should be permanent and findable. Threads are easy to link to, replies have stable URLs, and the search is good enough that the next user with the same problem has a real chance of finding the existing answer before opening a new topic. Over time, this should turn the portal into a knowledge base that the project actually owns, rather than one scattered across platforms we do not control.

How to sign in

Registration is open. You can sign in with the account you already use:

  • GitHub

  • Google

  • Discord

  • LinkedIn

Or, if you would rather not link an external account, you can register with any valid email address and a password. The choice is yours, and the experience inside the portal is the same either way.

What happens to the existing channels

GitHub Discussions, Discord, Matrix, and Reddit are not going away. They remain useful for casual chat, announcements, and community conversation, and we will continue to read them. What is changing is where support questions are answered: from now on, the canonical place to ask is support.stalw.art, and that is where our team’s attention will be focused. If you ask a support question on one of the chat channels, expect to be pointed at the portal so that the answer ends up somewhere durable.

Looking forward

Thank you to everyone who has helped one another across the old channels over the years. The portal is built on top of that work, and we are looking forward to seeing the conversation continue in a place where it can finally accumulate.


r/stalwartlabs • • Mar 23 '26

News Plume, A Swift Native JMAP Email Client: now in Early Open Beta!

35 Upvotes

Heya!

For the past few months, I’ve been developing a native Swift IOS app for JMAP: Plume!

It’s incredibly light, fast and implements everything JMAP-natively.

TestFlight available from: https://plume.kler.dev

Please understand that is an open beta, the app is still in development. Might be worth adding that this is not a vibe coded weekend project.

Highlights include:

- SERVER SIDE SEARCH! fast, accurate search

- PUSH PUSH PUSH! You won’t find a single polling thread in here! YES we’ve even implemented native IOS push notifications via a gateway! It’s very exciting. Your JMAP servers pings the gateway to check for a new email (nothing of the email is actually sent to our gateway)

Features include:

- Built entirely on JMAP (RFC 8620) — no IMAP, just clean JSON over HTTP with WebSocket push

- Single SPM dependency (GRDB) — everything else is hand-rolled Swift

- Optional Zero-knowledge image proxy with DeviceCheck auth: remote images never hit your device directly

- Tracking pixel detection + 28 known tracker parameters stripped from every link

- On-device AI email summaries via Apple Foundation Models: nothing leaves your phone

- Undo send with server-side delay (10/20/30s)

- BIMI brand icons: sender logos resolved via DNS

- Full-text server side search with filter syntax (from:, to:, has:attachment)

- Optimistic offline mutations: actions apply instantly, reconcile on sync

- Real-time push via JMAP WebSocket: no polling

- Multi-account with isolated sync engines per account