1.14.0 adds Mountain View, an optional layout with a top bar and a wide search field, and gives the Files app and the calendar a new layout. Dark mode now recolours messages instead of inverting them, so brand colours stay as sent. Push can be turned on for several accounts at once, and the account list can be backed up encrypted and restored on another device. The composer got a long round of send fixes. With several accounts open, batch actions no longer hit another account's message that happens to share an id with the selected one.
UI: Mountain View is an optional layout, chosen under Settings > Layout > Interface layout. On desktop it has a top bar with a wide search field, an apps grid and the account avatar in place of the left rail, a Compose pill, a docked compose window, an account popover with unread counts per account, and j/k, x and g shortcuts. Calendar, Contacts, Files and Settings use the same top bar. The default layout is unchanged (#1168, thanks @lucamzanon)
Files: The Files app is laid out like Contacts and Calendar: a sidebar with a New menu, the folder tree, Starred, Recent and Shared with me, a header with breadcrumbs, search, sort and a list/grid switch, a details panel, right-click menus in the sidebar and arrow-key navigation. On touch, a tap opens and a long press selects. Folders in the tree can be renamed and deleted, recent files open in their folder, and the view, sort and hidden-files settings now apply
Files: Image previews in Files and in attachments zoom with the wheel or a pinch, pan by dragging or with the arrow keys, switch between fit and full size on double-click, and close on a click beside the image
Files: Uploads show as a toast with their progress and a Cancel button. The large file warning is gone
Calendar: On wide screens, clicking or dragging over empty time opens a quick editor next to the slot, and clicking an event opens a card with edit, delete and more actions. Busy month cells show "+N more", the agenda lists each day as a row with the date beside its events, and the dates in the week header open that day. Calendar lists in the sidebar fold away and show their options on hover. Phones keep the full editor
Push: The notification settings list every connected account and turn push on or off for several of them at once, with a single browser permission prompt (#1054, thanks @lucamzanon)
Accounts: Users with several accounts can save their account list on the webmail server, encrypted in the browser with a password they choose, and restore it on another device. No mailbox passwords are stored. Admins turn it on with "Account List Backup" under Admin > Policy (#1201, thanks @lucamzanon)
Filters: The folder context menu runs a saved rule on the messages already in that folder (#1166)
Composer: Schedule send offers tomorrow at 8:00, tomorrow at 13:00 and next Monday at 8:00 as one-click times
Settings: The language picker has a search field and flags, and every date format and region option shows a live sample
Plugins: A plugin can run a message-list search with a JMAP Email filter (#1024)
Plugins: Calendar event actions get the event's uid and attendees, and the form-save hook now fires and can change the meeting link that is saved with the event (#1202, thanks @siennathesane)
Changes
Mail: Dark mode recolours messages instead of inverting them. Light backgrounds are darkened in their own hue, brand colours and dark fills stay as sent, and text only changes when it lacks contrast. A message's own dark mode styles follow the app theme instead of the system's
Mail: A folder opened before shows its messages at once and refreshes them in the background. A failed load leaves them on screen (#1195, thanks @lucamzanon)
Composer: On a phone, the formatting toolbar folds away behind a Formatting button and its buttons are larger. The send options sit together in one menu
Files: File sizes are rounded the way people read them, like 67 KB or 2.1 MB
Fixes
Mail: In views that merge several accounts, selecting a message no longer also selects a message with the same id in another account. Batch delete, move, archive, copy, mark read, tag and spam act only on the ticked messages, each through its own account. Before, a permanent delete could remove the other account's message (#1061, thanks @lucamzanon)
Mail: A message with two spaces in a row in its subject no longer freezes the tab (#1193, thanks @dealerweb)
Mail: Blocking remote content no longer drops a message's style sheets, so messages keep their layout before images are loaded
Mail: The list shows no paperclip on a message that only has inline images
Mail: The "via" badge is hidden when it repeats the sender's name, and a draft shows one Edit button
Send: Signed and encrypted messages reach Bcc recipients (#1206)
Send: After a plugin send, such as S/MIME, the composer closes, and a failed plugin send keeps the draft (#1207)
Send: A failed send removes its unsent copy from Drafts and explains the failure in plain words. A send while offline says so and keeps the message
Send: Typed addresses are split on commas, and a message with an invalid address is not sent. The address field marks which ones to fix
Send: Undo send disappears once the delay is over and says so when an undo fails
Send: Discard deletes the draft, also one reopened from Scheduled, and closing an autosaved draft no longer asks about unsaved changes
Send: The missing subject warning comes once per message, and the scheduled time shows its date, rounded to the minute
Composer: Tabbing into a reply or forward keeps the quoted message, and tabbing into a new message starts at the top of the body
Composer: A reply opens with the caret in the body, and the composer is titled Reply, Reply All or Forward instead of New Message
Composer: A reopened draft takes its text from the message body only. An attached .csv or .txt no longer turns up as the message text (#1213, thanks @shukiv)
Composer: A long attachment list scrolls instead of pushing the Send button out of view (#1212, thanks @shukiv)
Composer: The plain-text part of sent mail marks quotes and list items, and the signature has one separator
Composer: Enter adds the highlighted suggestion instead of the typed text, and the suggestions close once the address is added and line up with the typed text. On a phone they span the whole row and show the name above the address
Composer: Body text no longer shows through the pinned formatting toolbar (#1200, thanks @lucamzanon)
Composer: On a phone, sending a new message returns to the message list, and the Continue draft banner no longer flashes after a send
Composer: Bitwarden no longer offers logins in the recipient fields, the mail search box or the event participant field
Login: A remembered sign-in survives a server timeout or an origin switch during restore instead of signing out (#1167)
Login: Signing out of one account keeps the other accounts, and a reload with only accounts awaiting sign-in shows the login page instead of a connection error (#1201, thanks @lucamzanon)
Login: A reload without "Remember me" no longer reports an expired session
Unified: Switching to another account keeps the unified mailbox on (#1144, thanks @lucamzanon)
Push: Push is renewed for every connected account, not only the active one, so accounts no longer stop notifying after seven days. The all-accounts panel shows each account's real state and why an account failed (#1054, thanks @lucamzanon)
Filters: Forward is hidden in filter rules when the server allows no redirects (#1208)
Calendar: Save is blocked while a plugin's form-save hook runs, so a double press no longer creates the event twice (#1202, thanks @siennathesane)
Calendar: Participant suggestions are no longer cut off by the event form
Files: Canceling an upload stops the file in flight and says the upload was canceled instead of counting every file as done
Files: A drag selection keeps its items after letting go, and Files shows a loading state instead of "No files yet" before the first listing
Files: Read-only documents open in ONLYOFFICE without the -103 warning (#1187)
Contacts: The contact popover shows the sender's name when the saved contact has none, and Contacts shows placeholders instead of "No contacts yet" while loading
UI: The close and back buttons in the composer and conversation view have names for screen readers, and the composer toolbar buttons are labelled
UI: Reply All stays on one line in the conversation view, and the welcome banner gives touch tips on a phone
Translations: The new composer, send, Files and filter strings are translated
1.13.0 turns the vacation settings into an Out of Office page that can also forward mail, lets filter rules run only for a date range, and copies messages into another connected account. Files from the Files app can be attached in the composer, tags can be set on many messages at once and folders moved in bulk. Messages whose sender can't be verified now carry a warning. It also stops one account's settings and templates from carrying over to the next account signed in on the same browser.
Settings: Signing out and then signing in with another account in the same browser no longer shows the previous account's settings, tags and templates, and no longer saves them to the new account's synced settings (#1185)
Features
Mail: A message whose sender fails the From domain's checks, or that passes neither SPF nor DKIM, shows a warning above the message and a badge next to the sender. "Always trust this sender" is not offered for it
Mail: "Copy to account..." in the message menu files a copy in a folder of another connected account and leaves the original where it is. Copies keep their original date (#1150, thanks @lucamzanon)
Mail: The selection toolbar and the right-click menu put a tag on, or take it off, every selected message at once (#1077)
Out of office: The vacation settings are now an Out of Office page: the period on top, then the automatic reply and forwarding, each with its own switch. Forwarding works with or without the reply, never forwards spam and can keep a copy in the inbox. The reply can go to all senders, internal senders only or external senders only (#1152, thanks @dealerweb)
Filters: A rule can be limited to a period, and the rule list shows whether it is scheduled, active or expired (#1152, thanks @dealerweb)
Filters: The new "All messages" condition matches every message (#1154, thanks @dealerweb)
Composer: "Attach from Files" attaches files from the Files app, including files shared with you, without downloading and uploading them again (#1179)
Composer: Typing @ in the message body offers the To and Cc recipients and inserts the chosen one's first name. Bcc recipients are never offered. "Mention recipients with @" in the composer settings turns it off (#1134, thanks @dealerweb)
Folders: The folder settings can select several folders, with Shift+click for a range, and move them together under another folder or to the top level (#1173)
Calendar: The day and week views can be limited to working hours, and the week view can leave out non-working days. Events outside the visible hours are counted at the top and bottom of each day and open from there, and a button in the view shows all hours (#1164)
Login: LOGIN_SHOW_TOKEN_LOGIN=true offers "Sign in with an access token", for JMAP servers that take API tokens instead of passwords, such as Fastmail
Login: A server in JMAP_SERVERS whose oauth block has both a clientId and a buttonLabel gets its own sign-in button on the login page, even while OAuth is off globally. This allows a "Sign in with Google" button for a Gmail bridge next to servers that use passwords (#1135, thanks @lucamzanon)
UI: The browser tab title names the active account, as in "Inbox - alice@example.com - Webmail", and Settings, Calendar, Contacts and Files set their own title. Turning off "Subject in Tab Title" under Admin > Policy keeps message subjects out of the title, and with it out of the browser history (#1186, thanks @oguzyilmaznet)
Unified: While logins reconnect after a page load, the "All accounts" header shows how many are already counted, such as "4/10" (#1140, thanks @lucamzanon)
Push: A message that reaches several of your accounts rings once. For 30 seconds after an alert, notifications from your other accounts arrive silently (#1141, thanks @lucamzanon)
Server: SOURCE_CODE_URL points the source code link in Settings > About at the code of a modified build, as AGPL section 13 asks of anyone running one (#1161)
Changes
Calendar: The day and week views show 08:00 to 20:00 by default. "Limit visible hours" in the calendar settings changes the range or turns it off (#1164)
Tags: Tag views and their counts leave out messages in Trash and Spam (#1156)
Mail: In a shared mailbox, the Move menus list that mailbox's folders first and your own folders after them, under your account name (#1149)
Dependencies: sharp 0.35.5 and source-map-js 1.2.2 (npm audit)
Docs: CONTRIBUTING.md explains what belongs in core and what in a plugin, and which changes need an agreed issue before a pull request
Fixes
Mail: Attachments, inline images and .eml downloads of messages in shared folders are fetched from the account that owns them. On Cyrus they failed with "failed to find blob by id" (#1188, thanks @VasilisNtovolos)
Mail: Archive creates an Archive folder when the account has none, instead of only reporting that it is missing (#1176, thanks @cybersmurf)
Mail: Opening the webmail starts in the inbox instead of a folder from an earlier visit, such as Trash
Mail: Opening another folder, tag, account or search starts the message list at the top instead of where the last one was scrolled to (#1138, thanks @dealerweb)
Mail: Previews no longer start with a style sheet ("@media screen and ...") in the message list and in push notifications (#1136, thanks @lucamzanon)
Search: Searching from the unified All mail view finds sent mail too
Send: A draft saved without a subject reopens with an empty subject, so Send warns about it again instead of sending "(No Subject)" (#1189)
Send: When the server does not confirm that a message was sent, the draft is kept and the composer asks you to check Sent before sending again (#1019, thanks @lucamzanon)
Send: A message the server refuses shows the server's reason instead of "Invalid reference to non-existing object"
Send: A forward sent from another account's address no longer fails with blobNotFound. Its attachments and inline images are copied to the sending account first (#1142, thanks @lucamzanon)
Push: Push turns itself back on when the browser lost its subscription, retrying at most every 15 minutes (#1147, thanks @lucamzanon)
Push: The notification settings check push again when you come back to the tab, so push turned off in the browser or another tab no longer shows as on (#1148, thanks @lucamzanon)
Push: When the new message cannot be read, the notification no longer announces another unread message from the Inbox in its place, and a failed lookup shows a generic notification instead of none (#1041, thanks @lucamzanon)
Filters: "Stop processing further rules" also stops after "Delete silently" and "Reject", so later rules no longer file the message. Rules whose name contains double spaces, a tab or a trailing space no longer gain a copy on every save, and the copies earlier saves left are removed (#1153, thanks @dealerweb)
Filters: The forwarding limit counts the forwards one message can collect, instead of adding up every enabled rule, and warns when the rule order lets one message go over it (#1155, thanks @dealerweb)
Filters: A condition with several values wraps inside its card in the expanded rule view (#1139, thanks @dealerweb)
Filters: An open rule dialog or Sieve editor, and what was typed in it, survives a filter change from another tab or device (#1162, thanks @dealerweb)
Templates: A tab or device that had not reloaded no longer deletes templates created elsewhere when it saves its settings
Calendar: Duplicating an event keeps its meeting link (#1170)
Calendar: The Calendar Agenda plugin shows events from shared and group calendars (#1175, thanks @cybersmurf)
Files: Folders shared with you can be opened, and your own folders shared, in both folder layouts (#1181)
Files: Office editing works behind a reverse proxy where the editor and the browser reach the webmail at different addresses. The WOPI client URL may be an internal address (#1130)
Login: When Stalwart accepts a password and TOTP code but refuses to issue tokens, the login page points at the OAuth client settings instead of opening the browser's own sign-in dialog and reporting a wrong code
Branding: With APP_NAME set, the authenticator app entry for two-factor sign-in, the About card and the Files preview use the configured name and logo (#1177, thanks @cybersmurf)
UI: Text fields in dialogs keep the focus when the page behind updates, which happened every 30 seconds and on new mail (#1162, thanks @dealerweb)
UI: Buttons, menu items, tabs and other controls show the pointer cursor again (#1184, thanks @gwku)
UI: The account group labels in the From dropdown are readable in dark themes (#1157, thanks @dealerweb)
Translations: Czech is fully translated (#1174, thanks @cybersmurf), and missing placeholders are restored in Persian, Hebrew, Hungarian, Mongolian and Slovak (#1165, thanks @owgreen-dev)
1.12.0 lets you create filter rules straight from a message, offers the code from sign-in mail for one-click copying, and gives a phone linked with "Link Mobile App" a sign-in of its own, for every kind of account. Search now leaves Spam and Trash out by default, and emptying an ordinary folder moves its mail to the Trash. Browsers with many logins start up much faster. It also contains three security fixes, so please update.
Auth: The password+TOTP login no longer sends the webmail's OAuth client secret to a mail server the user chose when custom JMAP endpoints are allowed. Only admin-configured servers get it
Auth: The identity provider round trip for "Link Mobile App" can no longer sign the browser in as whichever account answered it, and a re-authentication the provider answered from an existing session instead of a fresh login is refused
Mail: Opening a previewed SVG attachment in a new tab no longer runs the sender's script in the webmail origin
Features
Mail: A "Rules" entry in the message right-click menu creates filter rules from the message: always move mail from its sender, domain or mailing list, mark it read, tag it, or block the sender. A new rule can also run on the messages already in the folder, and it can be undone from the toast
Filters: The rule editor suggests conditions from the message it was opened from (subject, recipients, List-Id, sender domain), and the new "is the address" and "has the domain" conditions match an address exactly
Mail: Codes from sign-in and confirmation mail show as a copy chip in the message, and in the list for a day. "Show Verification Codes" in settings turns it off
Mail: In the unified view, rows can be tinted with their account's color instead of showing the account dot, and tags this browser has no definition for, such as ones set by a server-side filter, get a color of their own instead of grey (#1052, thanks @lucamzanon)
Search: The folder dropdown shows subfolders indented under their parents
Composer: Pasted plain-text lines that start with "- ", "* ", "β’ ", "1. " or "1) " become real lists
Calendar: The event popover finds the join link of Teams, Zoom, Meet, Webex, Jitsi, Whereby and GoTo meetings in the location or description, and the location opens in maps, with a copy button next to it (#1095, thanks @lucletoffe)
Server: LIBRETRANSLATE_API_KEY_FILE, BULWARK_JWT_AUTH_SECRET_FILE, BULWARK_STALWART_MASTER_USER_FILE and BULWARK_STALWART_MASTER_PASSWORD_FILE read these secrets from files, and the admin config accepts oauthClientSecretFile and sessionSecretFile (#897, thanks @Cameo007)
Plugins: PLUGIN_SIGNING_KEY_FILE points at an existing Ed25519 key for signing managed plugin bundles, for read-only secret mounts (#996, thanks @mulatta)
Releases: Container images and release files come with a signed build provenance attestation, and the standalone tarballs get a .sha256. The README explains how to check them with gh attestation verify
Changes
Search: Search leaves out Spam and Trash unless "All folders" is picked in the folder dropdown. A search started in Spam or Trash searches that folder
Mail: Emptying a folder other than Trash or Junk moves its mail to the Trash, unless deleted mail is set to be removed permanently
Mobile app: "Link Mobile App" is offered for every account. Password and password+TOTP accounts confirm their password (and code) in the dialog. The phone gets a sign-in of its own, so signing out in the browser no longer signs it out, and a sign-in link to copy sits next to the QR code. Failed password attempts are limited per account
Calendar: Events are drawn as solid blocks of their calendar's color; declined and cancelled events are outlined
UI: Success and info toasts are plain cards; only errors and warnings are colored
Performance: With many logins, the browser restores them four at a time and no longer reloads every login's folders, identities and push connections each time one of them connects (#1132, thanks @lucamzanon)
License: An additional permission under section 7 of the AGPL allows distributing apps built from this code through app stores. It is provisional until every earlier contributor has agreed (consent request) and applies in full to contributions made since 30 September 2026
Docs: Guidelines for AI-assisted contributions in the README and CONTRIBUTING.md
Fixes
Mail: Clicking a folder's unread count lists only that folder's unread mail, not unread mail from every folder of every account
Mail: In "unread first" lists, a message you open stays in place until you open another
Mail: The message list no longer flashes a reload right after the page opens
Mail: On tablets, the message list comes back when the reading pane empties, for example after the open message is deleted or moved
Mail: Dividers between rows no longer disappear at 125% or 150% display scaling
Mail: The unread dot is centred on the sender avatar
Mail: Attachments declared with a type the browser cannot show, such as application/x-as400attachment from IBM i systems, application/x-pdf or a catch-all type, preview according to their file name (#1127, thanks @dealerweb)
Send: Recipients the server refuses at send are reported. When it refuses all of them, the message stays open as not sent; otherwise a warning names the refused ones (#1123)
Composer: The message body stays reachable in short reading panes, such as the bottom reading pane on a laptop (#1114)
Composer: Pasted plain text keeps its blank lines
Push: A notification link waits for a login that is still reconnecting instead of reporting the account as signed out, opens the folder of the right account, and no longer pulls you away from something you opened in the meantime (#1131, thanks @lucamzanon)
Push: A browser subscribed with another VAPID key resubscribes on its own, and Firefox can turn push on again when it cannot read its old subscription (#1121, thanks @shukiv)
Calendar: Dragged events no longer disappear: all-day events moved in the month view keep their start, and occurrences of recurring events with a UTC start, as in Google Calendar exports, no longer shift by the time-zone offset (#1119)
Calendar: A failed calendar fetch no longer hides every calendar or leaves only the birthday calendar selected
Calendar: The all-day area of the week view is sized from the visible week, stacks each day's tasks under its all-day events, and collapses to three rows with a toggle (#1122, thanks @sanitz)
Calendar: The Save button shows while an event is saving, and a save refused for its length says so (#921, thanks @texchi2)
Calendar: Invitations no longer carry blank participant names or an empty description (#748, thanks @lucletoffe)
Files: Files shared with you open and save in the office editor (#1094)
Files: Office editing works behind a reverse proxy at a sub-path (NEXT_PUBLIC_BASE_PATH) (#1101, thanks @dealerweb)
Accounts: Adding a sixth account through server-side SSO no longer overwrites the first account's sign-in
Mobile app: The app's "Sign in via webmail" keeps working past the first token expiry when the webmail uses a confidential OAuth client or an identity provider on another host
Admin: Admin sign-in works over plain HTTP. The admin cookie's Secure flag follows the request, or COOKIE_SECURE when it is set (#1063, thanks @lucamzanon)
Admin: The OAuth scope fields show the actual default scopes, which do not include offline_access (#1126)
Lite: SSO sessions survive a page reload with identity providers such as Rauthy that refuse an early token refresh (#552)
UI: The app retries loading its configuration after a timeout or a failed request instead of staying broken (#1112, thanks @theLucius7)
Docker: The image builds from a remote Git context (#1118)
1.11.2 fixes the Docker image and the standalone tarballs of 1.11.1, which did not start. It contains all the security fixes from 1.11.1, so please update.
Docker: The server starts again. 1.11.1 excluded data/ from the standalone build with a pattern that also dropped Next.js's own metadata modules, so the Docker image and the bulwark-standalone-* tarballs failed on startup. npm start and Bulwark Lite were not affected
1.11.1 is a security and bug-fix release. It fixes four reported vulnerabilities, two of them critical, and the findings of a security audit. Please update.
Admin: Only a Stalwart superuser gets into the admin dashboard. The admin probe accepted Stalwart's default tenant-admin role, so the administrator of a single tenant was signed into a dashboard that configures the whole instance (GHSA-v6hr-cmxm-pv73, thanks @douwezijlstra-frl)
Admin: Admin status is only taken from an admin-configured server. A context cookie minted while custom JMAP endpoints were allowed let a user-chosen server keep answering the admin probe after the switch was turned off (GHSA-j867-89p4-v8hm, thanks @Vip3r-MC)
API: Cross-origin writes are refused on the JMAP passthrough and every other cookie-authenticated /api/ route, not only /api/auth/*. A page on a same-site sibling origin could run arbitrary JMAP as the signed-in user. The gate also decides on the decoded path, so /api/%61uth/... no longer skips it (GHSA-9mvj-98f5-9q6g, thanks @kah-ja)
Admin: Admin sign-in attempts are also capped at 50 per 15 minutes across all clients, because a forged X-Forwarded-For got a fresh per-IP budget when no reverse proxy is in front (GHSA-7pj2-232x-6698, thanks @richardweinberger)
Admin: Impersonation no longer puts the Stalwart master password into the session cookie. The webmail creates an app password on the target mailbox that expires after 8 hours and is revoked on sign-out, and a used impersonation link is refused after a restart or on another replica
Mail: A sender can no longer fake a DMARC or DKIM pass in the security badges, with a crafted envelope address or an Authentication-Results header of their own
Mail: Opening an SVG attachment's thumbnail on its own no longer runs the sender's script in the webmail origin
Mail: Remote content stays blocked in the mobile conversation view, the .eml attachment preview, the quoted original of a reply or forward, the print view, and for URL spellings the filter missed (backslashes, CSS escapes, image-set())
Mail: Links in image maps (<area>) no longer keep window.opener
Mail: A mailto: unsubscribe goes to the single address in the link, and the confirmation shows recipient, subject and body before sending
Mail: A crafted winmail.dat no longer freezes the tab
Composer: A sender name containing a quote can no longer add a recipient to a draft
Filters: Rule names, header names and sizes are escaped, so rules from plugins or imported filter sets cannot add commands such as redirect to the Sieve script
Plugins: Hooks and slots need a declared permission, like the host API does. http.post can no longer reach the JMAP passthrough, /api/admin/*, /api/settings or other credentialed routes, and plugin storage is kept per account and deleted on sign-out
Themes: The theme CSS sanitizer no longer lets remote resources through (url(//host), CSS escapes, image-set(), @font-face sources), and theme CSS can only target :root and .dark, also when a plugin transforms it
Files: WOPI file downloads are always served as inert attachments, so a blob typed text/html cannot run script in the webmail origin
Server: The SSRF guard also blocks loopback beyond 127.0.0.1, CGNAT, benchmark, multicast and reserved ranges, and NAT64, 6to4 and Teredo addresses that wrap an internal IPv4 address. Telemetry targets are checked at connect time, and OAuth token and revocation requests never follow redirects
Auth: Synced settings are keyed on the account a bearer token belongs to, so on a multi-domain server john@b.example can no longer read the settings of john@a.example
Auth: Wrong passwords tried through the login pre-check are limited, so the route can no longer be used as a password oracle or trip Stalwart's ban against the webmail itself
Auth: Signing out ends office editor sessions opened in that browser, and a full sign-out clears search history, open tabs, Files recents, staged plugin uploads and the other accounts' leftovers, also in other open tabs
Auth: Signing out when another account's restore had failed no longer leaves that account resumable
Auth: The failed TOTP login no longer passes on a user-chosen server's error body
Server: Visitors who are not signed in no longer get detailed health data, the pending advisory text, the plugin list, the full admin policy or every server's domain list
Server: The setup token is passed in the URL fragment, so it stays out of access logs, referers and history
Server: Not-found pages outside the app tree cannot be framed, ALLOWED_FRAME_ANCESTORS never applies to the admin dashboard or setup, and sibling subdomains can no longer widen the sidebar-app frame-src
Server: Favicons are only served as raster images
Docker: Runtime state and secrets (data/, local-data/, .env) stay out of the standalone build and the image, and the mock JMAP server can no longer be switched on in a release build
Lite: Sign-out revokes the refresh token, and a failed token login no longer keeps the password in sessionStorage
Lite: Every static page gets a CSP that allows only its own inline scripts, the Stalwart bundle refuses to run inside a foreign frame, and the Stalwart install instructions use a tagged release with a published .sha256 instead of releases/latest
Features
Calendar: Tasks with a due date show in the month view (#1107)
Changes
Plugins: Hooks that change outgoing mail need email:send, and display takeovers need email:render-takeover. Plugins that don't declare them lose those hooks
Themes: Theme CSS keeps only :root and .dark rules plus @font-face, @keyframes, @media and @supports. Every url() except a #fragment is removed
Admin: Impersonated sessions end after 8 hours, and sessions minted by earlier versions are signed out
Server: /api/health?detailed=true needs a session
Fixes
Mail: The inbox keeps rendering when a message has an unparsable date (#1099)
Mail: A folder no longer switches back to the unified inbox while it loads (#1102, thanks @guisea)
Mail: Replies in the unified inbox come from the identity of the account that received the mail (#1104)
Mail: Mail deleted during a list refresh no longer reappears (#966)
Mail: The message list no longer jumps while attachment chips load
Mail: Scrolling the unified inbox, cross-account views, tag views and "All folders" search no longer skips messages
Mail: A failed message-list read keeps the list on screen instead of showing an empty folder
Mail: Marking read, starring, tagging, emptying a folder and cancelling a scheduled send report it when the server refuses them
Mail: Tagging and pinning write only the keywords that change, so they no longer mark mail unread that was read on another device
Mail: New mail and folder changes keep arriving for users with seven or more shared accounts
Send: A dropped connection can no longer send a message twice
Send: Undo and edit of a message sent from a group identity act in the group's own account
Composer: A reply draft stays in its thread when it is re-opened or undone
Composer: An open draft stays on its own account across an account switch
Accounts: Quick account switches no longer mix up identities, so replies go out with the right From address
Accounts: Folders, filters and the account security page no longer show the previous account's data after a switch
Accounts: Settings sync turns back on after signing out of one of several accounts
Push: A notification opens its message in the account it came from
Calendar: Events you declined show struck through (#1110)
Calendar: Daily recurring events keep going past a DST gap
Calendar: iCal subscriptions stay with the login that created them, so refreshing or removing one no longer touches a calendar of another login
Auth: SSO sign-in behind nginx no longer fails with a 502 when the cookies would overflow its header buffer (#1096)
Auth: Sign-in and addresses work on internationalized domains (#1100)
Lite: A rate-limited token endpoint no longer signs you out
i18n: The dark/light mode titles and the "Themes" settings tab are translated in every language (#1105, #1106, thanks @dulinux), and so is the themes settings panel
1.11.0 introduces Bulwark Lite, a static build of the webmail that runs without a Node server, and fixes many places where Bulwark and Stalwart disagreed about mail, filters, calendars, contacts and files. It contains everything from the three 1.11.0 betas. The latest Docker tag moves to this release.
Lite: Bulwark Lite, a static build that talks to the JMAP server straight from the browser. npm run build:lite builds it, and every release ships bulwark-lite-<version>.zip for any static web host
Lite: bulwark-lite-stalwart.zip, a Stalwart Application bundle that picks up its mount prefix at runtime, with OpenID Connect login
Mail: Toasts for mail actions that gave no feedback, and an "email sent" toast after immediate sends
Filters: "Keep a copy" option for forward actions
Filters: Per-rule "Also move messages marked as spam" option
Admin: Login page toggles in admin settings for the version, the 2FA code option, the heading and the subtitle (#1068)
Navigation: Deep links resolve to the local instance
Changes
Auth: Signing out of an SSO account also signs out of the identity provider when it advertises an end_session_endpoint (#905). Set OAUTH_END_SESSION=false to keep the provider session, for example when other apps share it, and OAUTH_POST_LOGOUT_REDIRECT_URI to send users back to the webmail afterwards
Filters: Move and copy rules leave mail that Stalwart marked as spam in Junk. Before, a rule such as "subject contains invoice" also collected phishing. "Keep" rules stay unguarded as the allow-list
Mail: Search sends terms exactly as typed, without adding a prefix wildcard, in tag views too
Mail: The message list loads attachment chips lazily, so large folders open faster (#1089)
Send: Scheduled send is limited to 7 days, the most Stalwart accepts
UI: Icon set migrated from Lucide to Tabler Icons
Docker: Pre-releases no longer move the latest tags
Docs: Most README details moved to the website docs, with new screenshots
Fixes
Mail: "All folders" search includes shared accounts (#1082)
Mail: A search inside a tag view narrows the tag instead of replacing it (#1084, thanks @rotterp)
Mail: Leaving an account drops a search folder scope that belongs to it (#1084, thanks @rotterp)
Mail: A failed search shows an error instead of "No results found"
Mail: In unified views, threads belong to the account that owns them (#1012, thanks @lucamzanon)
Mail: A failed move between accounts no longer deletes the original
Mail: "Mark all as read" marks every unread message instead of skipping pages past the first 500
Mail: Mark as spam and not spam report moves the server refuses
Mail: Unread-first order keeps bringing up unread mail past the first page
Mail: The tag button in a row's hover actions opens the tag picker instead of clearing the message's tags (#1032, thanks @douwezijlstra-frl)
Mail: Tag and tab counters no longer download every matching id
Mail: Text in fixed-width tables wraps to the screen on iOS (#1020)
Mail: Mail action toasts show again
Send: A send the server refuses is reported as failed instead of sent. When only filing the sent copy fails, a warning says so, so the mail is not sent twice
Send: A From override is also used as the envelope sender where the server accepts it. Where it doesn't, as on Stalwart, the composer says that the identity's address shows in the Return-Path (#1009)
Send: Sending with an identity adds its Bcc addresses
Composer: Attachments over the server's size limits are refused before upload
Composer: A staged attachment is kept until its upload finishes
Filters: Filters keep running while the auto-reply is on, and saving a filter no longer turns the auto-reply off
Filters: "Mark read", "star" and "add label" reach mail that the same rule moves, and rules keep their target folder after it is renamed
Filters: Forward actions respect the server's redirect limit (one on Stalwart) instead of being dropped without notice
Auto-reply: A warning appears before saving an auto-reply that Stalwart would refuse as too long
Calendar: Edits to a single occurrence stay on that occurrence and keep its details
Calendar: Date ranges are queried in the right time zone, and long recurring series or more than 1000 events no longer leave the calendar incomplete
Calendar: When the server refuses an event's invitations, you can save the event without sending them
Calendar: Subscriptions that fail with "Not authenticated" name the cause, such as a mail server certificate the webmail server does not trust (#1073)
Calendar: The subscription dialog no longer promises CalDAV URLs
Calendar: The import dropdown opens above the day and week views (#1049)
Contacts: Contacts with calendar, scheduling or free/busy links save, and cleared fields are cleared on the server
Contacts: vCard import no longer sends fields Stalwart rejects and writes addresses in the RFC 9553 form
Contacts: Deleting an address book that still holds contacts works
Files: Every file is listed, even when the account has more than maxObjectsInGet (#1069)
Files: Copying a folder copies its contents, and changes inside a shared drive go to the drive's account
Files: An upload or new folder whose name is taken becomes "name (2)" instead of failing
Files: Sharing works on Stalwart versions before 0.16.6
Files: Names Stalwart refuses are caught before sending, uploads store an accepted variant, and Office files keep their MIME type on Stalwart 0.16.6 and later
Account: You stay signed in after changing your password in settings
Account: Users who are not admins see their name, and changing the password or turning TOTP off asks for the current code
Auth: OAuth endpoints on the configured issuer's own host are accepted when they resolve to a private address, so split-DNS setups no longer need OAUTH_ALLOW_PRIVATE_ENDPOINTS (#1028)
Auth: Linking the mobile app re-authenticates against the account's own identity provider when OAuth is configured per server
Push: Push subscriptions are renewed before Stalwart's 7-day expiry, so a tab or app left open for over a week keeps getting notifications
Sharing: Principals are listed in directories with more than 500 users
Lite: The login page hides the server field once config.json sets jmapServerUrl (#1087)
Plugins: The plugin sandbox follows the app's "Automatic" language (#976, thanks @bartfaizoli76)
Pre-release. General test build for 1.11.0. Not recommended for production. The latest Docker tag stays on 1.10.0. To try it, use ghcr.io/bulwarkmail/webmail:1.11.0-beta.3.
Bulwark Lite zips (bulwark-lite-1.11.0-beta.3.zip for static hosts, bulwark-lite-stalwart.zip for Stalwart Applications) are attached once the Build Static Lite workflow finishes.
Added
Attendees can answer a single occurrence of a recurring event (#1086).
The message list loads attachment chips lazily, so large folders open faster (#1089).
Mail search sends search terms exactly as typed, without adding a prefix wildcard. This also applies in tag views.
Scheduled send is limited to 7 days, the most Stalwart accepts.
Fixed
"All folders" search includes shared accounts (#1082).
A search inside a tag view narrows the tag instead of replacing it.
Leaving an account drops a search folder scope that belongs to it (#1084, thanks @rotterp).
In unified views, threads belong to the account that owns them (#1012, thanks @lucamzanon).
A failed move between accounts no longer loses the message.
"Mark all as read" marks every unread message and reports spam moves the server refuses.
Send and search report failed JMAP calls instead of claiming success.
Attachments over the server's size limits are refused before upload.
Sending with an identity adds its Bcc addresses.
Tag and tab counters no longer download every matching id.
Mail filters keep running while the auto-reply is on, keep flags and folder targets when mail is moved, and no longer pull spam out of Junk.
A warning appears before you save an auto-reply that Stalwart would refuse as too long.
Edits to a single occurrence stay on that occurrence and keep its details.
Calendar ranges are queried in the right time zone and past the server limits.
If the server refuses an event's invitations, you are offered to save the event anyway.
The calendar subscription dialog no longer promises CalDAV URLs.
Contacts with calendar links or cleared fields save correctly.
Files lists every file, even when the account has more than maxObjectsInGet (#1069).
Files can copy whole folders and write to shared drives, shares files on Stalwart versions before 0.16.6, and handles file names and types Stalwart refuses.
You stay signed in after changing your password in settings.
Users who are not admins see their name and can change a TOTP password.
Push subscriptions are renewed before Stalwart's 7-day expiry.
Sharing lists principals in directories with more than 500 users.
The plugin sandbox follows the app's "Automatic" language (#976, thanks @bartfaizoli76).
Pre-release. This is a test build for Bulwark Lite. Not recommended for production. The latest Docker tag stays on 1.10.0. To try the server build, use ghcr.io/bulwarkmail/webmail:1.11.0-beta.2.
Bulwark Lite
bulwark-lite-1.11.0-beta.2.zip: unpack it on any static web host and set your JMAP server in config.json. The JMAP server must allow CORS from the webmail origin.
bulwark-lite-stalwart.zip: a Stalwart Application bundle that serves itself under the configured prefix (e.g. /webmail).
The Lite zips are attached once the Build Static Lite workflow finishes (a few minutes after publishing).
Changes since beta.1
Toasts for mail actions that had no feedback, plus an "email sent" toast after immediate sends
Toasts show again
Deep links resolve to the local instance
A staged attachment is kept until its upload finishes
Pre-release. This is a test build for Bulwark Lite. Not recommended for production. The latest Docker tag stays on 1.10.0. To try the server build, use ghcr.io/bulwarkmail/webmail:1.11.0-beta.1.
Bulwark Lite
Bulwark Lite is a static build of the webmail. It has no Node server: the browser talks to your JMAP server directly.
bulwark-lite-1.11.0-beta.1.zip: unpack it on any static web host (nginx, Caddy, S3, Pagesβ¦). Set your JMAP server in config.json. The JMAP server must allow CORS from the webmail origin.
bulwark-lite-stalwart.zip: a Stalwart Application bundle. Install it as an Application in Stalwart and it serves itself under the configured prefix (e.g. /webmail).
The Lite zips are attached once the Build Static Lite workflow finishes (a few minutes after publishing).
Please report problems with the Lite build in the issue tracker.
Also in this pre-release
New "Flat fields" theme
Icon set migrated from Lucide to Tabler Icons
Hardened Lite login, deep-link replay and settings gating
This release fixes six vulnerabilities reported by Jan Kahmen (turingpoint). Please update.
Mail: HTML mail could run script in the webmail origin. cid: references were rewritten to blob: URLs that kept the sender's Content-Type, and the reverse proxy skipped every security header β CSP included β on app paths whose last segment contains a dot, although the signed-in mail, calendar, contacts and files routes render there. Blob URLs are now retyped as inert, link clicks from the message frame are gated by scheme, and the proxy only skips headers for an explicit static-asset allowlist (GHSA-xvjh-v9c6-qcvc, thanks @kah-ja)
Auth: POST /api/auth/session and POST /api/auth/stalwart-context minted identity cookies without checking the supplied credentials against the JMAP server, so anyone could obtain a cookie for an arbitrary username and read that user's server-side settings. Credentials are now verified upstream before a cookie is issued: Basic credentials are bound to the account they authenticate, Bearer tokens to the session's username and identity. Deployments whose webmail container cannot reach the JMAP server no longer receive identity cookies, so cross-device settings sync stops working there (GHSA-wxcm-j4jc-9fxq, thanks @kah-ja)
Plugins: The /plugin-sandbox runtime trusted whichever window posted the first message, so a foreign site could window.open() it, post its own init, and run code under the route's unsafe-eval CSP in the app origin β with or without plugins enabled. The sandbox now only accepts its framing window, and the proxy refuses to serve the route outside an iframe or when plugins are disabled (GHSA-96cx-gx36-3g79, thanks @kah-ja)
Auth: Encrypted payloads now carry a purpose, so a WOPI editor access token can no longer be presented as a jmap_stalwart_ctx session cookie (GHSA-cqqx-mjcf-mh55, thanks @kah-ja)
Auth: Reject cross-site requests to /api/auth/*. A malicious page could POST the victim's browser into an attacker-controlled account (session fixation) (GHSA-qvr9-m8cq-7wvg, thanks @kah-ja)
Mail: Strip CR/LF and other control characters from the header values of generated read receipts. A crafted, RFC 2047-encoded subject could inject additional headers into the receipt (GHSA-w38p-hpqv-g89c, thanks @kah-ja)
Auth / Branding: Pin the resolved IP at connect time for stored custom JMAP endpoints and branding URLs too, closing the DNS-rebinding gap left after GHSA-24w9-8r42-8jwm
Features
Search: Global search across mail, contacts, calendar and files β query parser, ranking and cross-account providers, a search palette, and a search tab in the Pro shell with avatars, tinted icons and structured previews (#641); hits open through the owning login on every surface (#847)
Files: Office document editing through WOPI β Collabora Online, OnlyOffice and EuroOffice (#425); the demo Files drive ships office document fixtures and a word-processor icon
Calendar: Freely scrolling month, week and day views (#759), infinite scroll in the agenda view, and a setting to turn free scrolling off
Calendar: Recurring occurrences use Stalwart's synthetic ids (#140)
Calendar: Attendee free/busy via Principal/getAvailability
Calendar: Invitations and updates surface from CalendarEventNotification, and invitations are organized as the default ParticipantIdentity
Mail: Per-message HTML / plain-text toggle (#1022)
Mail: Share mail folders with other users via mail:share; share notifications appear as toasts
Mail: Search hits are highlighted with SearchSnippet/get
Mail: Pushes are resolved with Email/changes and Mailbox/changes deltas instead of refetching the list
Mail: Attachments show on list rows and open from there (#947, thanks @shukiv)
Mail: Filter advanced search by message size
Mail: Deleting a non-empty folder offers to delete its messages along with it
Mail: "Clear search when switching folders" setting (#852, thanks @shukiv)
CI: The test suite runs on every push and pull request (#647)
Dev: FileNode and blob round-trip in the mock JMAP server, and WOPI on the same-origin dev server
Changes
Docs: Installer section removed from the README and formatting cleaned up
Security policy: New vulnerability report email address
Fixes
Filters: Render the "Keep" action as fileinto "INBOX" (#1027)
Mail: List tagged mail from every account in the tag view, not just the selected folder's account (#1038)
Mail: Dragging a message out of the list could hang the tab when generated .eml file names collided (#1039)
Mail: Preserve label filtering during mailbox refreshes (#1017, thanks @ctaoist)
Mail: Hide body-embedded cid: parts declared as application/octet-stream from the attachment list (#1005, thanks @dealerweb)
Mail: Stop stretching images that carry their own max-width (#1034, thanks @shukiv), and let sender tables keep theirs (#790)
Mail: Refetch the body when JMAP truncates the displayed part instead of rendering a blank message (#928, thanks @hildebrandttk)
Mail: Auto-detect text direction in the read, print, plain-text, thread and .eml preview views (#663, thanks @shukiv)
Mail: Open search hits from shared folders in the right account (#923)
Mail: Collapse search hits for the same server object reached through several logins (#641)
Mail: Guard quick search against stale responses (#872, thanks @vj1235432)
Mail: Only show the unified mailbox section when it can be populated (#843), and stop it missing accounts that had not connected yet (#959, thanks @hildebrandttk)
Mail: Surface Email/set failures on delete and move (#956)
Mail: Report a missing archive mailbox instead of failing silently (#578), and archive shared-inbox mail into the owner's archive (#889)
Mail: Mark as spam from the viewer after the message left the list (#695)
Mail: Sort folders in the role assignment dropdown (#984)
Mail: Add the missing "Scheduled" folder role label (#495)
Mail: Wire the x shortcut to thread expansion (#683)
Mail: Keep the reading-mode toggle mounted so toolbar buttons stop jumping (#964)
Mail: Stop the batch toolbar hiding the message you just selected (#948, thanks @shukiv), keep rows in place when it opens, and drop the duplicate selection checkbox
Mail: Align the unread dot with the first line (#715, thanks @lucletoffe) and centre the sender avatar against the row (#953, thanks @shukiv)
Mail: Use Simplified Chinese for the selected-messages label (#786)
Send: Send from the address a message was delivered to (#991, thanks @rotterp)
Send: Set the answered flag when a reply goes out with a send delay (#985)
Composer: Upload attachments through the composing identity's account (#943)
Composer: Pro compose tabs default their From to the open mailbox (#990, thanks @rotterp)
Calendar: Jump to the day picked in the mini calendar (#1037, thanks @dealerweb)
Calendar: Wait for the JMAP client before loading the account principal, and recognise a refused principal read by its JMAP error type (#1036, thanks @dealerweb)
Calendar: Dedupe participants, resolve contact names across alias domains, and show the organizer's status (#986, thanks @ibayue)
Calendar: Linkify URLs in the event description (#968, thanks @lucletoffe)
Calendar: Normalize task progress states (#994, thanks @mulatta) and omit progressUpdated from the task completion payload (#958, thanks @sanitz)
Calendar: Preserve task alarms that the edit dialog does not show (#504)
Calendar: Report calendar clear failures instead of counting zero (#434)
Calendar: Make the iCal subscription size limit configurable and show the real error (#692)
Calendar: Paginate the event fetch on import so UID deduplication sees all existing events (#113)
Contacts: Strip the local-account prefix from address-book ids on contact update (#1043)
Contacts: Set name.full on all write paths so vCards carry the mandatory FN (#430)
Contacts: Create new contacts in the selected address book (#940, thanks @ponchofiesta)
Accounts: Detect HTTP/2 from the initial navigation timing so accounts are not capped at five (#1003, thanks @lucamzanon)
Auth: Reject wrong passwords server-side so the browser never shows its Basic Auth dialog (#969)
Auth: Stop retrying token refreshes that fail permanently (#972)
Auth: Normalize the OAuth discovery base so a session JMAP_SERVER_URL refreshes (#971, thanks @thejdubb02)
Auth: Use the selected server's issuer for SSO discovery (#952)
Auth: Retry the JMAP session fetch when a redirect drops the auth header (#892)
Auth: Drop max_age=0 from OIDC re-authentication requests (#938)
Auth: Require a session for the translate API (#903)
Mobile: Keep the actions panel below the status bar and pad attachment preview overlays for the iOS PWA safe area (#936)
Mobile: Render plain-text-only mail as text in the thread view (#489)
Mobile: Stop the More menu flashing open when a message is opened
Mobile: Dismiss the search panel once a search runs, open the folder drawer from the right in RTL (#944, thanks @shukiv), and keep the account switcher header on screen
PWA: Focus the client before navigating on notification click (#914, thanks @bitfactory-dk)
Push: Resolve push previews for shared and group mailboxes (#839)
Plugins: Allow sandbox chunk loading with CORS (#922, thanks @mulatta)
Plugins: Fail fast when the plugin storage database is blocked (#840)
Plugins: Refill missing managed bundles from the server (#636)
Plugins: Translations for plugins installed from the marketplace (#939, thanks @paulhenry46)
Settings: Index newer settings and the flat calendar toggles in the settings search
Settings: Invalidate the persisted update status after an upgrade
UI: Readable native <select> option lists in dark themes (#999)
i18n: German update (#1031, thanks @GyroGearl00se), "Forward as attachment" in more languages (#1016, thanks @dulinux), toolbar keys for nb and zh-TW (thanks @ibayue), and managed sidebar-app keys for zh-TW
i18n: Preserve locale cookie precedence and normalize Chinese proxy locale detection (thanks @kchuang1015); keep basePath when normalizing a Chinese Accept-Language
Calendar / Auth: Pin the resolved IP address at socket-connect time when fetching caller-supplied URLs (iCalendar subscriptions, JMAP login and TOTP token-exchange servers). The public-host check used to run before fetch() opened its socket, so an attacker who controlled DNS for a hostname could rebind it to loopback, RFC-1918 or cloud-metadata addresses between the check and the connect and read up to 10 MB of the internal response through the unauthenticated /api/fetch-ical endpoint. Redirect targets are now validated the same way (GHSA-24w9-8r42-8jwm, thanks @Tike00)
Features
Push: Re-sync existing push registrations in the background on app start, so registrations created before the delivery filter existed β or whose Junk mailbox id went stale β get repaired without re-enabling notifications
Fixes
Push: Stop sending notifications for spam β the push subscription now carries a JMAP emailPush delivery filter that excludes $junk and the Junk mailbox (needs a server advertising the emailPush capability, e.g. Stalwart β₯ 0.16.16; older servers keep the previous behaviour)
Navigation: Forward every request header through the proxy β Next 16.3's RSC header check rejected the stripped router headers and sent navigations into a 307 redirect loop (#919)
Calendar: Stop emitting RSCALE=GREGORIAN;SKIP=OMIT on plain Gregorian recurrence rules β DAVx5 rejected them as invalid and Android sync broke (#805, thanks @hildebrandttk)
Mail: Search suggestions with recent searches and contact autocomplete (#845)
Mail: Render plain-text emails in the app font by default, with a monospace option (#830)
Mail: Pull-to-refresh indicator while dragging the list down (#826)
Mail: Redesigned unread favicon badge β a compact keyline badge
Mobile: Swipe message rows left or right for quick actions β archive, delete, toggle read, toggle star, or spam, configurable per direction, RTL-aware
Pro: Reworked split-screen shell with per-pane tab strips, drag & drop between panes, and pane-scoped overlays
Pro: Folder tabs via drag & drop
Pro: The address bar follows the focused tab, and deep links are delivered live to already-mounted surfaces
Composer: Real byte progress for attachment uploads, stock and plugin-offloaded; cancel now aborts the transfer itself
Calendar: Moving an event's start moves the end with it, keeping the event's length
Contacts: Trusted Senders address book enabled by default on contacts-capable accounts
Settings: Custom time zone setting that overrides browser detection (#755)
Login: Server dropdown on the OAuth-only login screen (#799)
Push: New-mail notifications grouped per account with a "+N more messages" line instead of one notification per message
Push: Per-device revoke for push subscriptions (#841)
Admin: Configurable Stalwart admin access to the dashboard (#870)
Admin: Push relay picked from an admin-defined list instead of a free URL field
Branding: OpenGraph/Twitter link previews with a generated card image
Plugins: onBeforeComposeOpenToReply and sibling hooks let a plugin edit an email before it populates the composer for reply or forward
Plugins: getPublicKeyFromWKD
Plugins: Privileged plugins can fetch a byte range of a blob
Plugins: progressFileId on api.http.post so an offloaded upload reports byte progress to the composer chip
Plugins: isActive on AccountResponse
Plugins: Label settings and label reordering exposed to extensions
Plugins: JMAP keyword helpers and gateway keywords exposed to extensions
Plugins: Mailbox refresh hook
Dev: AddressBook/set in the dev mock JMAP server
Changes
Plugins: The PRF getOrCreate flow is split into separate get and create steps for better authenticator compatibility (#851)
Dependencies: Next 16.3.3, pdfjs-dist 6.2.108, DOMPurify 3.4.14 (npm audit)
Fixes
Send: Route scheduled sends to the account that owns the submission β mail scheduled from a shared address can now be listed, cancelled, and rescheduled instead of going out silently (#874)
Send: Split recipient lists whose angle brackets never close without dropping recipients
Send: generateMessageId crashed on insecure origins (crypto.randomUUID undefined), failing the send after the draft save
Composer: Keep attachments when re-opening a draft, also in the Pro draft tab, and destroy old draft versions only after a successful create or send (#849)
Composer: Keep the signature in saved drafts and embed it into re-opened drafts (#848)
Composer: Double-click unlocks the embedded signature for editing
Composer: Keep already written text when applying a template (#540)
Composer: Preselect the shared folder's identity for new messages
Composer: Namespace all accounts consistently in the Pro composer identity list
Composer: Opening a mailto: link runs the unsaved-draft dialog instead of replacing the draft outright
Composer: Keep the fresh-compose tab title clear of the selected email subject
Composer: Clear the viewer when sending destroys the displayed draft
Reply: Honour an external Reply-To even on a self-sent message
Mail: Flip $junk/$notjunk keywords on spam and not-spam (#850)
Mail: Remove keywords with null rather than false in Email/set, per RFC 8620
Mail: Escape the JSON Pointer in keyword patches so nested tags like work/clients patch the right keyword
Mail: Route keyword writes β tags, pins, flags β to the selected shared account so they persist
Mail: Route shared-folder management to the owner account, scoped to one server
Mail: Open the right conversations in a shared mailbox (#814)
Mail: Route multi-account email lookups and invitation parsing by source account (#847), and parse invitations in directly viewed shared folders against the folder owner (#867)
Mail: Folders containing a system folder name no longer disappear from the sidebar (#771)
Mail: Folder drag & drop can move folders into other parents (#855)
Mail: Keep the folder tree when a refresh burst hits maxConcurrentRequests (#780)
Mail: Stop All-Mail and cross-account views emptying on delete, star, or mark-read (#791)
Mail: Keep just-read or unstarred mail in the open Unread/Starred view
Mail: Search folder filter defaults to all folders and persists (#788)
Mail: Fixed-width read/unread toolbar button so buttons don't jump when a message opens (#864)
Mail: Transparent hover-action background on tagged rows
Mail: Enforce the external media preference on plugin-rendered bodies (#797)
Mail: Apply the data: URI allowlist to media tags and srcset candidates too
Mail: Make label reordering atomic
Mail: Pass PDF bytes to pdf.js instead of fetching the blob: URL that CSP connect-src blocks (#871)
Mail: Include email templates in cross-device settings sync (#825)
Mail: Toast store crashed on insecure origins, breaking every post-action acknowledgement
Mobile: Reach the tag and move submenus in the more-actions menu (#779)
Mobile: iOS Safari no longer zooms the viewport on every input focus (#838)
Calendar: Click and double-click create events at the clicked slot instead of near the current time
Calendar: Save and Cancel are available when an event is created, and the toolbar no longer overflows in edit mode
Calendar: Edit a single recurring occurrence via a one-shot override patch
Calendar: Hide tasks-only calendars from the event calendar
Calendar: Exclude subscription and read-only calendars from event creation (#762)
Calendar: Rights-first event editability, including alias organizers
Calendar: Pin supported-calendar-component-set on created calendars (#760)
Calendar: Gate first-touch calendar and contacts requests to stop duplicate default calendars (#907)
Calendar/Contacts: Namespace all accounts consistently on switch, so multi-account address-book aggregation and calendar selection survive an account switch
Contacts: Import vCard dates as RFC 9553 PartialDate and map common X- extensions (#224)
Files: Decode percent-encoded FileNode names from WebDAV-created nodes (#869)
Files: Reset the account-scoped Files drive on every account switch
Accounts: Refresh the account display name from the Stalwart principal on login, restore, and switch (#900)
Auth: Reuse the cached access token on session restore (#552)
Auth: Refresh TOTP-minted tokens with the default client id when no OAuth client is configured (#873)
Auth: Store the session cookie for relative JMAP server URLs
Security: Close IPv6 transition-address and redirect bypasses in the endpoint guard
Security: Add embedded custom app origins to CSP frame-src (#787)
Security: Per-account isolation for encryption at rest
JMAP: Cap live SSE streams per tab and keep exactly one stream per client, so many logins can't starve JMAP requests (#702)
JMAP: Check the specific capability a request declares (principals:owner), not a broader one
JMAP: Resolve relative session URLs without corrupting URI templates
Settings: Merge per-account maps on server load, fixing the compose identity switch
Settings: Leaving the Pro interface returns to the surface in use and keeps the settings scroll position
UI: Position portalled popovers before first paint to stop the layout flash on open
Push: Recreate the push subscription on re-register (#841)
i18n: Key parity across all 24 locales, Catalan and Mongolian registered in the client provider, and scoped translation hooks instead of relative namespace paths
Docs: Lengthen the example SESSION_SECRET so it meets the minimum length
A GitHub Actions incident left the 1.8.0 release build queued indefinitely, so no Docker image was ever published for that tag. 1.8.1 re-runs the release with the same code, plus one plugin slot that merged in the meantime.
Security: Manage S/MIME and PGP public keys and configure Stalwart encryption at rest from account security settings
Notifications: Background notification onboarding, sequenced after the PWA install prompt
Navigation: Deep links for mail, calendar, contacts, files, and settings, with screen-reader improvements
Settings: Always show the Unified Mailbox switch in Layout settings
i18n: Catalan translation
i18n: Localized editor toolbar across every locale
Plugins: Contact API β contact.get, contact.create, contact.update, contact.search
Plugins: contact-cryptokeys UI slot, behind the ui:contact-cryptokeys permission, so a plugin can render a contact's crypto keys in place of the built-in list
Plugins: user.getAccounts and user.getIdentities
Plugins: user.logout method and logout hook
Plugins: Crypto API β public-key management and encryption-at-rest control on the privileged tier
Plugins: onBeforeBlobUpload can offload an attachment to external storage
Plugins: Binary Blob/File bodies for api.http.post
Plugins: upfiles.get moved behind email:blob-read, off the privileged tier
Dev: Mock JMAP defaults now include nested tags
Changes
Mail: The "Reset to defaults" button is gone from tag settings β one stray click wiped a carefully built tag list, with no confirmation and no undo
Fixes
Send: Send through the identity's own account client so DKIM matches the From domain (#461)
Send: Split Name <addr> recipients into the JMAP name and email fields (#672)
Send: Time out stalled JMAP requests so a send can't hang forever (#702)
Mail: Keep inline images when replying to application/octet-stream cid parts (#543)
Mail: Reply on your own thread message no longer re-addresses the original recipients (#703)
Mail: Empty folder no longer stops after 500 emails (#711)
Mail: Move messages across accounts from the "Move to" context menu, preserving read state and deferring source removal to Stalwart
Mail: Stop resurrecting deleted rows in the mailbox refresh merge
Mail: Keep the message/rfc822 attachment visible after inline unwrapping
Mail: Strip sender and recipient names from forward-as-attachment filenames
Mail: Open mailto: links in the built-in composer
Mail: Spell out the full tag path in drag-and-drop toasts, so Personal/Receipts and Work/Receipts no longer read as the same tag
Mail: Act on current email state in the context menu's mark-as-read instead of a stale copy
Mail: Match the selected-row tint between dark and light mode
Mail: Restore lost animations after the Tailwind config move
Drafts: Restore the sender identity when reopening a draft
Calendar: Stop re-adding the organizer to the attendee list on every save (#731)
Calendar: Stop re-probing shared accounts that have no calendar access
Calendar: Route the parse dump through the debug logger
S/MIME: The built-in S/MIME implementation has been removed from core and re-delivered through the new generic crypto plugin hooks (privileged same-origin plugin tier). S/MIME signing, encryption, decryption, certificate management, and the related settings UI now live in a plugin rather than the main app. Deployments that relied on built-in S/MIME must install the S/MIME crypto plugin to retain those features.
Opt-in tabbed multi-pane interface for power users. Open multiple mail, calendar, contacts, and file views side-by-side, drag tabs to reorder or split panes at the edges, and work across all logged-in accounts in one shell, cross-account email moves, a unified inbox with search, account-split calendar/contacts/files sidebars, and a per-account "From" dropdown in the composer. Enable from Settings β Appearance; the proInterface preference is per-device and not synced.
Pro mode is experimental and we need your feedback to shape it. If something feels off, breaks, or is missing, please don't hesitate to open an issue or start a discussion on GitHub!
Breaking Changes
Plugins: Plugins now run inside a null-origin iframe sandbox and talk to the host over a postMessage RPC bridge. The in-process plugin runtime is gone; the bundled in-tree plugins have been migrated. Third-party plugins built against the old in-process API need to be ported to the sandboxed runtime.
Plugins: Server-managed bundles must be Ed25519-signed by the host and approved by an admin before they load. The host public key is served from /api/plugin-signing-pubkey and each bundle response carries the signature in the X-Bundle-Signature header. User-uploaded bundles still load unsigned, but managed marketplace and dev-folder bundles do not.
Plugins: bundleHash is now a full SHA-256 over the bundle. Legacy short hashes are migrated on first load; any out-of-band tooling that pinned the old hash format needs to be updated.
Features
Pro: Tabbed shell with drag-to-reorder, drag-to-edge to split, side-by-side panes, and pane-aware responsive layout with a scoped sidebar overlay
Pro: Auto-redirect to the Pro shell when Pro mode is on; proInterface is kept per-device instead of syncing
Pro: Multi-account mail sidebar with client routing and a per-account mailbox cache
Pro: Unified mailbox always visible, with full-text search
Pro: Cross-account email moves
Pro: Multi-account calendar sidebar split into owned vs shared per account
Pro: Multi-account contacts and a cross-account file picker
Pro: Composer From dropdown grouped by account
Plugins: Per-plugin admin approval workflow with Ed25519 bundle signing verified on load
Plugins: Marketplace update flow for installed plugins and themes
Setup: Allow the setup wizard over plain HTTP with a dismissable warning gate
Setup: Warn when the JMAP URL points at a local-only host
Account: List and reorder logged-in accounts from settings (#282)
Mail: Mobile handoff page with JMAP authentication verification for cross-device OAuth
First-launch web setup wizard. New installs no longer need to hand-edit .env.local - point a browser at the container and the wizard probes the JMAP server(s), configures OAuth/OIDC, generates the session secret, accepts branding uploads, and provisions the initial admin password. Admin storage is now split into ADMIN_CONFIG_DIR (operator-authored, mountable read-only after setup) and ADMIN_STATE_DIR (runtime audit log and login timestamps); the legacy ADMIN_DATA_DIR keeps working for existing installs.
Features
Setup: Web setup wizard with multi-step flow: Server, Auth, Security, Logging, Branding, Review, Admin
Setup: Admin config/state directory split with optional ADMIN_CONFIG_READONLY for immutable deployments (#226)
Setup: File uploads on the wizard branding step
Setup: Redesigned review step with grouped summary and an advanced toggle for the full config
Setup: Require explicit confirmation when JMAP probe finds no session
Mail: Drag attachments out of the viewer to the local file system (#267)
Mail: Configurable signature position β above or below quoted text (#266)
Mail: Signature position is now searchable from the email behavior settings
Mail: Show avatar in Focused list for compact density and above
Mail: Align Focused list preview with other layout previews
Compose: From-header override in the composer with catch-all auto-reply, replies to an alias on a domain you own pre-fill the alias as the sender even when it isn't a configured identity (#246)
Performance
Mail: Prefetch initial email data on login
Auth: Parallelize login round-trips and drop redundant JMAP re-verify
Fixes
Auth: Skip upstream JMAP reverify for trusted URLs (#237)
Auth: Show account identity in the switcher header instead of the sending alias
Compose: Fall back to the primary identity signature on reply
Setup: Drop redundant first-login banner about removing ADMIN_PASSWORD (#222)
UI: Consistent notice cards for server probe results