Our fixes accepted by SnappyMail and Tachyon

· Convergent · 5 min

We build our mail service on free software. When we find a defect, we fix it on our side, then offer the fix to the original project: every one of its users benefits, and we have no divergence to maintain. In October 2026, twelve of these proposals were accepted by two projects: SnappyMail and Tachyon.

SnappyMail is a free, lightweight and fast webmail maintained by the-djmaze. Tachyon is a fork of it, released under the AGPL and maintained by kimusan. The two share most of their code, so the same defect often sits on both sides — and the same fix can serve both.

We thank their maintainers for their reviews and for the welcome given to these contributions.

Data reliability: the address book

A sync with one empty side no longer wipes the other. CardDAV sync treated an empty list as an instruction: if the server returned zero contacts, previously synced local contacts were deleted; if the local store was empty, the server was pruned. An empty list is far more often a fault — a server index needing a rebuild, an unparsable response, a transient authentication problem — than a genuine request to delete everything. The fix skips deletions in whichever direction the source is empty and lets import and export reconcile both sides. SnappyMail #2060 · Tachyon #125

GetContactByEmail() queried the wrong columns. Looking a contact up by address returned the right data but none of its sync metadata (id, modification date, etag), with three PHP warnings per call. Nothing a user would notice, wrong values for a CardDAV sync. The contact is now read back by its id, with every column. SnappyMail #2074

HTTP headers passed as an associative array are sent correctly. Several callers handed over their headers as name => value, where curl expects Name: value lines: they went out malformed and were dropped. The most visible consequence was that uploading a contact to the CardDAV server was refused. Both transports now build their header lines in one place, with a test. Tachyon #124

The photo stored on a contact is used as its avatar. The avatars plugin listed the contacts lookup as “TODO”. It is now in place: the contact’s photo, in its three vCard forms (data: URI, encoded binary, URL), comes before Gravatar, BIMI or the site favicon. A photo the user chose outranks a guessed avatar, and a sender whose card carries a photo no longer triggers any request to outside services. SnappyMail #2061

Security

Every data-* attribute is stripped from message HTML, not just the first one. Message HTML sanitising meant to remove all data-* attributes. But the loop removed the attribute it was standing on, inside a live collection: iteration stopped there, and exactly one data-* went per element, however many it carried. The fix, measured attribute by attribute, gives the code the behaviour its comment promised. SnappyMail #2075 · Tachyon #126

Hardened two-factor authentication. Tachyon’s two-factor-auth plugin moves to 2.20.0:

  • the TOTP secret is sealed (libsodium); backup codes are kept only as HMAC hashes and shown once;
  • a code that was already accepted cannot be replayed;
  • five failures in fifteen minutes lock the second factor for fifteen minutes;
  • the QR code, which encodes the secret, is no longer returned when the second-factor status is queried.

Accounts already enrolled keep their second factor: legacy records are read, then sealed on first read. Tests come with every security check. Tachyon #128

Interoperability: calendar, directory, headers

Calendar invitations sent as attachments are recognised more widely. An invitation from Evolution or Exchange often carries its calendar part inline, and the attachment as application/ics. The ics-viewer plugin only looked for text/calendar and showed nothing. It now recognises text/calendar, application/ics, text/x-vcalendar and .ics files. SnappyMail #2064 · Tachyon #127

Contact suggestions search several directory branches. The ldap-contacts-suggestions plugin queried a single base DN. A directory rarely keeps everything in one place: people in one branch, meeting rooms in another. The base_dn setting now accepts several branches separated by |, for example ou=People,dc=example,dc=com|ou=Resources,dc=example,dc=com. An existing value behaves exactly as before, and a failing branch no longer stops the others from answering. SnappyMail #2070

A folded subject keeps its space. When a long subject is folded right before an encoded word (RFC 2047), the space in front of it was dropped when the message was opened: “vos équipements” became “voséquipements”. RFC 2047 and RFC 5322 only allow that trim between two encoded words; the fix sticks to that. Tachyon #170

Where these fixes stand

ProjectAcceptedReleased
SnappyMail#2060, #2061, #2064, #2070, #2074, #2075 — merged on 22 October 2026Not yet: the latest release, 2.38.2, dates from October 2024
Tachyon#124, #125, #126, #127, #128 — merged on 7 October 2026Yes, since Tachyon 4.4.2
Tachyon#170 — merged on 22 October 2026Not yet in a release

A merged change is not yet a shipped change: it becomes one with each project’s next release.

Why give back upstream

A fix kept to yourself has to be reapplied at every update, and only protects your own users. Given back, it is reviewed by the people who know the code best, it protects every installation, and it costs nothing more to maintain. Each of these proposals describes the defect, how to reproduce it and the test that keeps it closed: that is what lets a maintainer accept it with confidence.


Looking for a free-software webmail with calendars and contacts, run by a team that knows its code? See our services or let’s talk. And follow us on GitHub.