VersionDrift
Product

Stalwart Mail Server 0.16.20 → 1.0.0

8 releases · 0 breaking changes · 1 security fix · 0 advisories fixed

1security0breaking0deprecated39feature115fix

Breaking changes

None in the release notes.

Security changes

  • security OAuth: The OAuth Profile for Open Public Clients is now fully enforced. If you use an external OIDC provider, make sure it conforms to the draft; otherwise mail clients such as Apple Mail will stop working with it as they adopt the profile. Ask your provider to add support, or, at your own risk,… in 1.0.0

Every release in between

featureFull-text search:
featureText extraction and indexing of PDF, Microsoft Office (docx, xlsx, pptx), OpenDocument (odt, ods, odp), EPUB and RTF documents found in attachments and files.
featureFull-text search of file storage contents.
featureIndex queue with per-partition status, concurrency control, retries and index rebuilds.
featureMetadata:
featureJMAP Object Metadata (draft-ietf-jmap-metadata-04) for e-mails, mailboxes, Sieve scripts, calendars, events, address books, contacts and files.
featureIMAP METADATA extension (RFC 5464).
featureDKIM2:
featurePost-quantum composite signatures using ML-DSA-44 and Ed25519 with SHA-512.
featureConfigurable Message-Instance hash algorithms (SHA-256 and SHA-512).
featureOAuth and OIDC:
featureOAuth Client ID Metadata Documents (draft-ietf-oauth-client-id-metadata-document-02).
featureOAuth scopes (mail, contacts, calendars and files) per client and grant.
featureConnected applications: users can list and revoke the applications authorized to access their account.
featureFirst-party clients, post-logout redirect URIs and per-client grant types.
featureExternal OIDC directories: audience matching, opaque token validation via UserInfo or token introspection and a policy for unscoped tokens.
featureCaching:
featureRedesigned cache subsystem with a dedicated tier for large accounts.
featureCache snapshots can be swapped to local disk, Redis or the blob store.
featureBuilt-in local in-memory store for single-node deployments.
featureAzure Blob Storage authentication using shared keys, SAS tokens, client secrets, managed identities or workload identities.
featureSecondary LDAP servers for failover.
featureSSRF protection for outbound HTTP requests (webhooks, OIDC discovery, AI and DNS providers).
featureConfigurable client IP source (X-Forwarded-For or Forwarded) and number of trusted proxy hops.
featureMemory budget for buffered data.
featureConfigurable limits for e-mail headers, keywords and mailboxes per message, calendars, address books and spam filter text processing.
featureTLS options for Kafka and NATS cluster coordinators.
featureAutomatic cleanup of per-user data after a share is revoked.
featureAI (Enterprise):
featureAI models from multiple providers: OpenAI-compatible endpoints (OpenAI, Azure, vLLM, llama.cpp), Anthropic, Google Gemini and Ollama, with per-model rate limits, concurrency caps, timeouts and reasoning levels.
featureAI spam classifier with structured verdicts, model tiers with escalation, training from AI verdicts and optional web search.
featureNew database layout: the key layout was redesigned and the database schema version is now tied to the edition (AGPL-only or AGPL/SEL). Data must be migrated from v0.16.x using the migration tool.
otherSettings schema: multiple objects, fields and enumeration values were renamed, restructured (AI models, OIDC directories, Azure Blob Storage, HTTP forwarded headers) or removed, and internal identifiers were renumbered. See SCHEMA_CHANGES.md for the full list.
featureTelemetry: New events and metrics were added and event identifiers were renumbered. See SCHEMA_CHANGES.md.
securityOAuth: The OAuth Profile for Open Public Clients is now fully enforced. If you use an external OIDC provider, make sure it conforms to the draft; otherwise mail clients such as Apple Mail will stop working with it as they adopt the profile. Ask your provider to add support, or, at your own risk,…
otherUpdated IETF drafts:
otherDKIM2 to draft-ietf-dkim-dkim2-spec-07.
otherJMAP for Calendars to draft-ietf-jmap-calendars-33.
featureDevelopment: Test suites (unit, integration, conformance and stress) are now kept in a private repository, and new features are developed privately before release. The source code of every release remains public under the AGPL-3.0. Learn why
fixMultiple bug and security fixes.
+5 more items in the release notes
fixIMAP:
fixAPPEND and COPY/MOVE into another account's mailbox do not change the JMAP EmailDelivery state.
fixSEARCH RETURN with MIN or MAX alongside ALL returns only the minimum and maximum in ALL, and SAVE combined with MIN or MAX and ALL or COUNT saves only the minimum and maximum instead of every match.
fixiTIP: Merging a received update does not copy the VTIMEZONE definitions it carries, so an occurrence moved by an external organizer can reference a TZID (for example W. Europe Standard Time) the stored event no longer defines, and Apple Calendar shows both the original and the moved occurrence.
fixOAuth: Loopback redirect URIs without a path (for example http://127.0.0.1:45678) are not matched against registered loopback URIs on any port and are rejected by dynamic client registration.
fixWebDAV: PROPFIND allprop returns DAV:current-user-principal, so Windows Explorer shows every file and folder under the account name.
featuresd_notify support.
featureJMAP: Mailbox/set allows moving a mailbox under a parent that already has a child with the same name, and rejects renames that only change the case of a mailbox name with alreadyExists.
fixDNSSEC: Use hickory-resolver patched fork at https://github.com/stalwartlabs/hickory-dns.
fixRocksDB: Blob garbage collection rewrites every stored blob on each compaction of the blobs column family, about once per 128MB of new mail.
fixMilter: End-of-headers is not sent to milters that negotiate SMFIP_NOHDRS without SMFIP_NOEOH, so the transaction stalls until it times out.
fixOIDC: When the access token lacks the claimUsername claim but has an email claim, the account is built from email without consulting the userinfo endpoint.
fixJMAP:
fixonSuccessUpdateEmail and onSuccessDestroyEmail entries in EmailSubmission/set that name an existing submission by id update or destroy the Email with the same id instead of the submission's own Email.
fixA VacationResponse/set that only sets isEnabled on an account without a vacation response activates an empty Sieve script.
fixJMAP: Creating a MaskedEmail with emailDomain fails with forbidden for every domain when the account has addresses on more than one domain.
fixAutodiscover: Requests for a response schema other than Outlook's, such as ActiveSync (mobilesync), are answered with the Outlook settings instead of error 601.
fixIMAP:
fixLOGIN and AUTHENTICATE with a wrong, expired or unknown app password or API key are answered with an untagged NO, so clients keep waiting for the command to complete until the connection times out.
fixThe failed login that exceeds the maximum number of authentication failures is answered with an untagged NO before the connection is closed.
fixDKIM:
fixA rotation moves the active key to retiring even when its successor fails to publish or propagate, so outgoing mail is sent unsigned until a retry publishes the new key. The DNS write failure is also not logged and the task reports success.
fixKeys created while DNS management was manual, or before DKIM was added to the published records, are never rotated after DNS management becomes automatic. Domains already affected start rotating once a DkimManagement task is created for them.
fixAfter switching DNS management from automatic to manual, a due rotation activates a new key that was never published in DNS, so signatures fail verification, and retiring the old key is retried forever.
fixSpam filter:
fixMessages with no text line long enough for a Pyzor digest are checked with the digest of empty input and tagged PYZOR.
fixDNSBL answers with several return codes, such as a Spamhaus ZEN listing in both SBL and PBL, are scored for only the first code returned.
fixDNSBL lookups that return "not listed" are cached for 24 hours regardless of the zone's negative TTL.
fixRemoving a duplicate training sample of a message reclassified on the same day clears the blob link of the sample that is kept.
fixMTA: Queue quotas with an empty match expression are never enforced, including the global queue quota created on first start.
fixRocksDB: The info log (LOG, LOG.old.*) grows without limit because log rotation and retention are left at RocksDB defaults.
fixWebUI: A blob store read error at startup, such as an S3 authentication failure, stops the web interface from being downloaded.
featureDNS: PowerDNS Authoritative provider for automatic DNS record management.
fixTroubleshoot tool: TLSA records are looked up for every MX host, including hosts whose zone is not DNSSEC signed.
fixSpam filter:
fixOpenPhish and PhishTank entries containing uppercase characters never match, since message URLs are lowercased while HTTP lookup entries keep their original case. HTTP lookups now match keys case-insensitively.
fixURL shortener links are followed using the lowercased URL, so case-sensitive short links resolve to the wrong destination or not at all.
fixIncremental training never advances its position past the first run, so every retained sample added since then is trained again, and counted again in the reservoir, on each run until it expires.
fixUpdating the rules only adds new objects, so upstream changes to existing rules, DNSBL servers, HTTP lookups, lookup keys and file extensions never reach an existing installation.
fixUpdating the rules reports success when objects fail to import, or when a configuration error stops the updated settings from being activated.
fixJMAP:
fixA PushSubscription created within the verification rate limit window of another one on the same account never receives its PushVerification, since the blocked verification is dropped instead of being sent once the window expires.
fixA push notification retried after a failed delivery can report an older state than a change queued during the failed attempt, since the older state changes are merged last and overwrite the newer ones.
fixChanges made while a push request is in flight are not delivered until the next change reaches the same subscription, since a successful delivery cancels the pending retry.
fixThe VAPID aud claim is derived from a hand-written parse of the push URL, so a crafted push URL can make the server sign a token for a push service other than the one the request is sent to.
fixEmail/import rejects a blobId that refers to a Blob/upload creation id in the same request ("#u0") with Invalid blob id..
fixEmail/set with a full mailboxIds object identical to the current mailboxes, together with a keyword change, stores the message with IMAP UID 0, so IMAP clients stop seeing it.
fixMTA:
fixA node without the outboundMta role stops replying to DATA and to JMAP submissions once about 1024 messages have been queued on it.
fixMX records are resolved through the DNSSEC-validating resolver even when DANE is disabled.
fixA DATA stage Sieve script does not see headers added by milters or MTA hooks, and discards every milter and MTA hook change when it edits the message.
fixMySQL: Range deletions and search index removals start with a single unbounded DELETE and switch to chunks only after a timeout.
fixIMAP: COPY and MOVE fail with NO [CONTACTADMIN] when another session changes the same message at the same time.
fixAutodiscover: Implicit TLS ports (993, 995, 465) are advertised with TLS, which Outlook reads as STARTTLS.
fixHTTP: Idle keep-alive connections are never closed.
featureExpressions: bit_and function.
fixMTA:
fixA mailing list whose recipients include another mailing list is accepted at RCPT TO and then rejected at local delivery with 550 5.5.0 Mailbox not found.
fixDMARC aggregate reports carry two spf elements per record and the version element of a DMARC aggregate report is written as 1 instead of 1.0.
fixDSNs generated for an alias rewrite or a list expansion emit a doubled addr-type in Original-Recipient (rfc822;rfc822;[email protected]).
fixDSNs that cannot be written to the store are discarded, the recipients are flagged as notified and the original message is removed from the queue, losing both the bounce and the message.
fixPOP3:
fixTOP msg n counts the n lines from the first byte of the message instead of from the first byte of the body.
fixA message whose very first line begins with . is not byte-stuffed.
fixSpam filter: Moving or copying a message from one account into another creates no training sample, so the classifier never learns from it.
fixSieve: envelope "orcpt" yields the bare address for an ORCPT supplied over SMTP. It now carries the addr-type prefix in every case, as required by RFC 6009.
fixACME: The _acme-challenge TXT records published for a DNS-01 authorization are never removed.
fixDNS: The DNSSEC resolver queries a single nameserver at a time, working around a hickory-resolver race that cancels the TCP retry when two nameservers return a truncated response in parallel.
fixTroubleshoot tool:
fixMX records are resolved through the DNSSEC-validating resolver, matching the resolver used by the delivery path.
fixA TLSA lookup that fails or returns bogus records stops the delivery attempt for that host, instead of continuing without DANE.
fixOIDC: Bearer tokens that carry no email, preferred_username or upn claim are always authenticated against the default directory.
fixMeilisearch: A confirmation timeout is treated as a failed write even when failOnTimeout is disabled, so an index whose batches take longer than pollInterval x maxRetries never completes an indexing task and resubmits the same batch indefinitely.
fixWebUI: A failed update no longer takes an Application offline.
fixFoundationDB: The cached read version is invalidated when any broadcast is received from another node.
fixRedis:
fixOn a cluster, the rate limiter and the blob upload quota issue INCR and EXPIRE as a MULTI/EXEC transaction, whose MOVED redirects collapse into a single EXECABORT that never refreshes the slot map.
fixA connection that fails because it is addressing the wrong server is returned to the pool and reused, since the recycle check only issues PING.
fixWebDAV: PROPFIND responses do not declare the namespace of every requested property on the DAV:multistatus element.
fixMTA:
fixSMTP session state obtained before STARTTLS is discarded once the TLS handshake completes, as required by RFC 3207.
fixInbound throttle, outbound throttle and queue quota match expressions evaluate their if/then conditions.
fixDMARC: Messages without an aligned SPF or DKIM pass from a domain that publishes a DMARC policy are reported as fail instead of none in logs.
fixDMARC: A temporary DNS error on an SPF or DKIM check whose identifier aligns with the author domain produces temperror instead of fail.
fixDMARC: A policy record without a p tag is only applied, as p=none, when it contains a rua tag, even if it declares sp or np.
fixiMIP: Notification subjects and bodies omit the event start date, time and timezone for recurring events.
fixOIDC: Discovery is retried for 30 seconds before the directory is marked as unavailable, so a provider that is still starting up no longer requires a restart.
fixWebUI: The cached bundle of an Application is discarded when its resourceUrl changes or the record is deleted, so the next unpack fetches the new resource.
fixDNS: Append . to MX record hostnames.
fixRecovery mode: Automatic IP banning is disabled during recovery and initial setup.
fixFoundationDB: The cached read version is invalidated when a queue refresh or registry change broadcast is received.
fixSieve: Messages filed with fileinto by a user script are treated as ham and are no longer moved to Junk when classified as spam.
fixIMAP:
fixSELECT, EXAMINE and STATUS on a shared mailbox require the r (read) right.
fixAUTHENTICATE is refused on clear-text connections unless allowPlainTextAuth is enabled, and the greeting and CAPABILITY response advertise LOGINDISABLED instead of the AUTH= mechanisms in that case.
fixIMAP, POP3, ManageSieve: Partial commands received before STARTTLS or STLS are discarded once the TLS handshake completes.
fixNetwork: Inbound TLS handshakes, both implicit and STARTTLS, are aborted after the listener's tlsTimeout, releasing the connection slot.
fixOAuth: A random key is used when the OIDC provider's encryptionKey or HMAC signatureKey cannot be read, instead of an empty key.
fixLDAP: The {local} and {domain} filter placeholders are escaped.
fixJMAP:
fixCalendarEvent/set stores useDefaultAlerts per user, and CalendarEvent/get returns false when it was never set.
fixCalendarEvent/get returns null for baseEventId unless the id is a synthetic recurrence instance.
fixCalendarEvent/get returns null for requested recurrenceRule and recurrenceOverrides properties on synthetic recurrence instances.
fixCalendarEvent/get and ContactCard/get return only id when the properties argument is an empty list, instead of every property.
featureSieve: Messages generated by user scripts are DKIM signed through the new SieveUserInterpreter.dkimSignDomain setting, which defaults to the account's own domain.
fixJMAP:
fixCalendarEvent/set requests that ask for scheduling messages are rejected with a forbidden error when the account cannot send them.
fixCalendar/get and AddressBook/get return every property when the properties argument is omitted or null.
fixEventSource ping events advertise the interval in seconds rather than milliseconds.
fixCalendar synthetic ids returned when expanding recurrences identify an occurrence by its recurrence id.
fixIMAP: Every command in a pipelined STATUS or FETCH batch receives its tagged completion, instead of the first failing command dropping the responses for all commands queued behind it.
fixWebDAV: Accounts without a storage quota no longer advertise a 4 GiB limit in DAV:quota-available-bytes.
fixMTA: Inbound DMARC and TLS aggregate reports that a reporter sends more than once are imported again as a duplicate entry.
fixSpam filter: Domain and URL blocklists are queried only for text written as a link.
fixiTIP: Detaching an occurrence that the recurrence rule already generates is sent as a METHOD:REQUEST carrying the RECURRENCE-ID instead of a METHOD:ADD.
fixSieve: fileinto :specialuse and specialuse_exists accept special-use attributes in the \Trash form.
fixLDAP: Active Directory servers that answer an unauthenticated bind (a non-empty DN with a zero-length password) with success no longer authenticate accounts without a password.
fixNetwork: Listeners bound to the unspecified IPv6 address ([::]) fall back to IPv4 when socket creation fails with EPROTONOSUPPORT.
fixOpenTelemetry: log exporter does not include the parent span's attributes.
See how far your stack has drifted from upstream. Release data from vendors’ own sources · feed under CC BY 4.0 · Code under AGPL-3.0 Settings Feed status