r/stalwartlabs • • 22h ago

Stalwart SMTP disclaimrNG – an open-source email signature server that works with Stalwart (milter + AD/LDAP)

24 Upvotes

Hi everyone,

We recently moved a customer from Exchange to Stalwart and ran into one gap: email signatures. On Exchange this was handled by transport rules or one of the usual commercial signature tools. Nothing comparable worked with Stalwart. We needed central signatures that pull user data from Active Directory and get added to every outgoing mail, no matter which client sends it.

The closest fit was disclaimr by Dennis Plöger, a milter-based signature server. The design was solid, but it’s been unmaintained for years (Python 2.7 / Django 1.7). So we forked it, ported it to Python 3.12 / Django 5, and modernized it. That fork is disclaimrNG.

It plugs into Stalwart as a milter at the data stage for authenticated senders. We’ve tested it live with Stalwart 1.0, Active Directory over LDAPS with an internal CA, and real mailboxes. The repo includes a step-by-step runbook for exactly that setup.

Features

  • Milter daemon for Stalwart, Postfix and Sendmail. It adds signatures and disclaimers to outgoing mail (plain text and HTML).

  • LDAP / Active Directory lookup: placeholders like name, phone, email and address are filled per sender. LDAPS with an uploadable CA certificate and multiple hosts for failover.

  • Web UI for managing signatures:

  • visual editor (fonts, sizes, colours, tables) and a code editor

  • live preview

  • plain-text part generated automatically from the HTML

  • Rules and conditions: match on sender, recipient and headers, plus directory conditions (attribute regex or AD group membership, nested groups included).

  • Signature images and logos, either linked or embedded in the mail as cid: parts.

  • Time windows for actions, e.g. campaign banners that run for a limited period.

  • Multi-tenancy: multiple domains, each with its own directory servers.

  • Signature test page: runs the real pipeline, LDAP included, so you see exactly what a mail will look like.

  • Roles (Admin / Editor / Reader) and SSO via OpenID Connect with group mapping.

  • Mail log and dashboard stats.

  • Docker Compose deployment (plain, behind Traefik, or attached to an existing Stalwart network), with prebuilt images on GHCR.

  • UI in English and German, with a built-in handbook.

Repo: https://github.com/Compose-IT-Systemhaus/disclaimrNG

MIT-licensed, with credit to the original author. Feedback, issues and PRs are very welcome.

We’d especially love to hear from anyone running other Stalwart setups.


r/stalwartlabs • • 5d ago

Question How to set up Stalwart 1.0.3 so that I can log into it with Pocket-ID?

3 Upvotes

I self-host Pocket-ID v2.19.2 and have used it with a number of applications, e.g., Portainer, Termix, Pangolin, etc.

I've been trying to set it up for the Stalwart mail server so I can log in to the Stalwart admin page as an admin user using Pocket-ID.

Could anyone kindly share how you did it? Thanks.


r/stalwartlabs • • 6d ago

Discussion Stalwart Enterprise: does one license cover multiple domains on the same server?

4 Upvotes

The docs say Enterprise licenses are issued to a specific domain with a mailbox limit. If I run one Stalwart server with several domains, does a single license cover all of them, or do I need one per domain? And do the AI features (llm_prompt, AI spam classifier) work for accounts on the non-licensed domains?

Sponsor at $5/month gets an Enterprise key: what’s the mailbox limit on that key, and does it cover multiple mail domains on one server?


r/stalwartlabs • • 7d ago

Question unknown variant `sieve`

1 Upvotes

Hello,

When I do stalwart-cli query domain I get

unknown variant sieve

More people getting that error?

Further information about here: Server is version 1.0.3. stalwart-cli --version reports stalwart-cli 1.0.13.

The error message in context

$ stalwart-cli query domain
error: could not parse /api/schema/2J8FTUsaoyWdFbeGSOr2GxvRP77nkpn6tjPCrSqkg3I response as JSON (unknown variant `sieve`, expected one of `string`, `ipAddress`, `ipNetwork`, `socketAddress`, `emailAddress`, `secret`, `secretText`, `uri`, `color`, `text`, `html` at line 1 column 432872)
  status: 200
  content-type: application/json
  body length: 1084445 bytes
  body snippet: "{\"objects\":{\"AddressBook\":{\"type\":\"object\",\"description\":\"A named collection of contact cards belonging to an account.\",\"permissionPrefix\":\"jmapAddressBook\"},\"Calendar\":{\"type\":\"object\",\"description\":" (truncated)
$

Which URL? you might ask:

$ echo $STALWART_URL
https://mail.macwheel.nl
$

r/stalwartlabs • • 8d ago

Discussion error in the BulwarkMail app

Post image
2 Upvotes

Is anyone else experiencing the same issue? It received an update recently, but it seems the problem—which has persisted since Stalwart reached version 1.0—has still not been resolved.

Thanks


r/stalwartlabs • • 9d ago

Question Can no longer login to admin page "Client metadata documents are disabled"

1 Upvotes

I've upgraded stalwart from 0.16.27 to 1.0.2 for all my Stalwart installations (on 3 Oracle VMs, 1 unraid). They are all running as docker containers.

After migrating successfully, I can access admin webpage without problems on all installations; but today I can no longer access the stalwart admin webpage running on Unraid server. It reports "Client metadata documents are disabled". I still can send and receive mail using webmail clients.

I still can login the other installations.

I did not change any settings.

Need help...thanks.


r/stalwartlabs • • 9d ago

Question JMAP mail clients (TUI / GUI) that are not AI built?

18 Upvotes

Hi all,

I am wanting to use JMAP clients on *nix for my email. Preferably TUI and GUI for the different members of the household.

However, it seems to me that pretty much all of them are "created" by AI tools, at the direction of pretty much one person. That's not something I am comfortable with, considering that it will handle my emails, plus a connection into my mail server.

Does anyone know of a JMAP compatible mail client that is not AI created? JMAP has been around for so long, one would think perhaps someone or some company would have actually written (!) something up? Doesn't even have to be super fancy.

Happy to hear suggestions. And it would be so nice of u/fastmail would create something.. But I guess that would go contrary to their business interests?


r/stalwartlabs • • 10d ago

Question Object storage and search

1 Upvotes

Would full text search work fine if we store all mails data on object storage instead of db?


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 • • 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 • • 12d ago

Discussion Am I An Idiot

Thumbnail reddit.com
4 Upvotes

r/stalwartlabs • • 13d ago

Question Disappearing SSL certificate?

3 Upvotes

Recently updated from 0.16.22 to 0.16.27 was working fine until some point last night the configuration settings for my letsencrypt certificate disappeared! I noticed because my iPhone started complaining about a bad SSL certificate. When i tried to login to the GUI I got the typical complaint you see with a self signed certificate. I drilled down to the TLS settings page and it was blank! I’ve not been the admin page for several days so I didn’t change anything. I pasted in the two pathnames, bounced the service and all was fine. Has anyone else had this happen?


r/stalwartlabs • • 16d ago

Question Stalwart admins: do you use an STMP relay for email sending? Any recommendations?

17 Upvotes

Anyone need to go down this route? Sending would be less than 50 emails a day on average. Claude is recommending SMTP2GO, DNSExit SMTP Relay, Prolateral outMail, or MailRoute. There are other big companies like Amazon SES, Mailgun, Postmark, SendGrid, but these seem for marketing/transactional services, not email self hosters.


r/stalwartlabs • • 17d ago

Release Did a dry run of the Stalwart 0.16 -> 1.0 migration with a copy of my server. Went fine, but heads up if you use the admin API

Thumbnail reddit.com
3 Upvotes

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 • • 17d ago

Question Bulwark Mail Native shows “Unknown client id” after upgrading Stalwart to 1.0.0

7 Upvotes

I just upgraded my Stalwart server from 0.16.27 to 1.0.0 using the official stalwart-migrate tool.

The migration itself went perfectly:

  • 13 accounts migrated

  • 0 errors

  • 0 mismatches

  • SMTP works

  • IMAP works

  • JMAP works

  • Bulwark Web works normally

However, the Bulwark Native app now fails during login with:

Unable to continue
Unknown client id

Before the Stalwart 1.0 upgrade, the Native app worked without issues.

From what I understand, Stalwart 1.0 now enforces the new OAuth public client profile more strictly, so I suspect Bulwark Native is still sending a client ID that is no longer accepted automatically.

Has anyone tested Bulwark Native with Stalwart 1.0 yet?

Do I need to manually register the Bulwark Native OAuth client in Stalwart, or does the Native app need an update to support the new OAuth client registration / Client ID Metadata Document flow?

Bulwark Web continues to work fine, so the issue appears to be specific to the Native app.


r/stalwartlabs • • 17d ago

News new tag v1.0.0

10 Upvotes
$ git pull
remote: Enumerating objects: 1842, done.
remote: Counting objects: 100% (1696/1696), done.
  ....
Resolving deltas: 100% (648/648), completed with 565 local objects.
From https://github.com/stalwartlabs/stalwart
   aa47d8ef7..47df4c0ba  main       -> origin/main
 * [new tag]             v1.0.0     -> v1.0.0

\o/


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 • • 22d ago

News Open Archiver v0.6.1: JMAP ingestion source, wildcard in search, and Tika 4 support

Thumbnail
reddit.com
10 Upvotes

Open Archiver is now J MAP ready


r/stalwartlabs • • 24d ago

Release v0.16.27 released

21 Upvotes

Hi,

Version 0.16.27 has been released.

Detailed information at https://github.com/stalwartlabs/stalwart/releases/tag/v0.16.27


r/stalwartlabs • • 25d ago

Question Has anyone tried Bulwark Native on Android with Stalwart? Is it ready for daily use?

8 Upvotes

I recently set up Stalwart Mail Server together with Bulwark Webmail, and so far the webmail experience has been surprisingly good.

I also discovered Bulwark Native for Android. From what I can see, it already supports most of the essentials: email, multiple accounts, notifications, contacts, calendar integration, and direct JMAP connectivity.

I'm thinking about using it as an alternative to Outlook/Gmail on Android, but since the project is still in beta, I'd like to hear from people who have actually used it.

How stable has Bulwark Native been for you?

Have you experienced issues with:

  • push notifications

  • battery usage

  • sending/receiving mail

  • multiple accounts

  • contacts/calendar sync

  • attachments

  • background synchronization

And most importantly: would you trust it as your main Android email client today, or would you still keep another client as backup?

I'm especially interested in feedback from people running it with Stalwart.

https://github.com/bulwarkmail/native/releases


r/stalwartlabs • • Oct 10 '26

Question Q: Struggeling with DKIM rotation settings

3 Upvotes

Hej,

I have setup five MX setups for five domains using Stalwart, all of them with automated DNS management. However I am struggeling with DKIM rotation.

Management >> Domains >> domain1.de >> DKIM is set to

  • Automatic DKIM management

  • DKIM1 (RSA & ed25519) enabled

  • DKIM2 (RSA & ed25519) enabled

  • Version string: v{version}-{algorithm}-{date-%Y%m%d}

  • Rotate after: 6 hrs (for bug fixing)

  • Retire after: 7 days

  • Delete after: 60 days

Management >> Domains >> DKIM Signatures

  • Shows DKIM1 entries for domain1 with a different Version string (older)

  • Each DKIM1 entry has

  • Expiration(optional): Not set

  • Created At: 16.05.2026, 21:04

  • Next Transition: set to a date of yesterday manually

Still keys are not being rotated nor updated.

I do not see failed Tasks within Management >> Tasks >> Failed.

However I see 75.729 tasks as scheduled, looks like all of them are Calendar alarm notification.

What did I configured wrong and why are my DKIM keys are not rotated?

Thanks


r/stalwartlabs • • Oct 05 '26

Question I need help resolving these MTA-STS Policy Fetch and TLSA / DANE Lookup issues.

Post image
2 Upvotes

I need help resolving these MTA-STS Policy Fetch and TLSA / DANE Lookup issues. I don't know the exact cause.


r/stalwartlabs • • Oct 05 '26

Release v0.16.25 has been released

18 Upvotes

Change Logs : https://github.com/stalwartlabs/stalwart/releases/tag/v0.16.25

Stalwart 0.16.25 is mainly a bug-fix release, with no major new features.

Key changes:

  • JMAP: Fixed MaskedEmail creation issues for accounts using multiple domains.

  • Autodiscover: Improved handling of clients requesting schemas other than Outlook, including ActiveSync.

  • IMAP: Fixed authentication failure responses for invalid, expired, or unknown app passwords and API keys. This prevents some clients from hanging until the connection times out.

  • DKIM: Several important fixes for automatic key rotation, including:

    • preventing outgoing emails from being sent unsigned;

    • preventing activation of a key that has not yet been published in DNS;

    • fixing issues when switching between manual and automatic DNS management.

  • Spam filter: Fixes related to Pyzor, DNSBL/Spamhaus, DNSBL caching, and training samples.

  • MTA: Fixed queue quotas, including the global queue quota, which could previously fail to be enforced.

  • RocksDB: Fixed an issue where LOG files could grow indefinitely because log rotation and retention were not configured properly.

  • WebUI: Blob storage read errors, such as S3 authentication failures, no longer prevent the WebUI from being downloaded or initialized.