Library / SDK
ProtonMail/gluon avatar
ProtonMail/gluon

Gluon builds an IMAP4rev1 server out of a Go event pipeline

An IMAP server library written in Go

555 stars43 forksGoMIT

At a glance

What is it?
Gluon is ProtonMail's MIT-licensed Go library for running an IMAP4rev1 server, structured around a builder that wires a connector, a database, a watcher and a reporter together. It suits a team that wants to put a real mail server under its own control, and it does not suit a team that wants a hosted mail product.
Who is it for?
Gluon is a good fit for a team that needs to own its mail infrastructure, such as a lab testing mail clients, an appliance vendor embedding IMAP, or an organisation that must keep message state inside its own database rather than behind a vendor API.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A library, not a mail server product

Gluon is described as an IMAP4rev1 library focused on correctness, stability and performance, and the repository makes the distinction clear in its structure. There is no packaged daemon and no service to install. What exists is a Go module, github.com/ProtonMail/gluon, that you wire into your own binary: server.go and builder.go sit at the top level next to connector/, store/, watcher/, reporter/ and the imap/ package, so the pieces of a server are visible as separate directories you configure. The consequence for an adopter is that you own the process, the storage and the lifecycle, and the library is the engine rather than the product. That is a good deal for an organisation that already has those responsibilities and a bad deal for anyone hoping to install a mail server and get an inbox.

Running the demo server and what it prints

The repository ships a demo server in demo/demo.go, started with a log level set in the environment:

bash
$ GLUON_LOG_LEVEL=trace go run demo/demo.go
DEBU[0000] Applying update                               update="MailboxCreated: Mailbox.ID = 0, Mailbox.Name = INBOX" user-id=ac8970c5-cdb7-4043-ad85-ad9b9defcfb8
INFO[0000] User added to server                          userID=ac8970c5-cdb7-4043-ad85-ad9b9defcfb8
INFO[0000] Server is listening on 127.0.0.1:1143

That trace output is worth reading closely, because it documents the internal update model. The server applies updates, and those updates are typed, here MailboxCreated and MessagesCreated, and each carries a user identifier. The server listens on the standard IMAP port on the loopback interface in the demo. The demo creates two users, both with the password pass, the first holding the addresses [email protected] and [email protected] and the second [email protected] and [email protected], which gives you an account and an alias to test address routing with.

What the login capability line actually promises

Connecting to the demo with telnet shows the greeting a client sees, and the capability set is the contract you will be held to:

bash
$ telnet 127.0.0.1 1143
* OK [CAPABILITY IDLE IMAP4rev1 MOVE UIDPLUS UNSELECT]  00.00.00 - gluon session ID 2
tag login [email protected] pass
tag OK [CAPABILITY IDLE IMAP4rev1 MOVE UIDPLUS UNSELECT] Logged in

IDLE, MOVE, UIDPLUS and UNSELECT are advertised alongside IMAP4rev1, and each one removes a workaround from a client's behaviour. UIDPLUS in particular means the server can return APPENDUID values, so a client can track the UID of a message it just appended instead of guessing. The banner also carries a session identifier, which is the handle you use when correlating log lines with a connection. The practical check for an adopter is to compare that capability line against what your clients need, because a client that assumes an extension the server does not advertise is a client that will break in a way that is tedious to debug.

Append, select, fetch and the state a mail client relies on

A full session in the README shows the sequence a real client performs. APPEND stores a message and returns an APPENDUID, SELECT opens the mailbox and answers with the flag set, the message counts, the permitted permanent flags, and the UIDNEXT and UIDVALIDITY values:

bash
tag append inbox (\Seen) {14}
+ Ready
To: [email protected]
tag OK [APPENDUID 1 1] APPEND
tag select inbox
* OK [UIDNEXT 2] Predicted next UID
* OK [UIDVALIDITY 1] UIDs valid
tag OK [READ-WRITE] SELECT

UIDVALIDITY is the field that matters most for client caching: when it changes, a client is expected to discard its cached state, so an implementation that resets it carelessly will make every client resynchronize. UIDNEXT tells the client what to expect next. The fetch in the transcript uses BODY.PEEK, which retrieves a body without setting the Seen flag, which is what keeps a client from marking mail as read just by previewing it. The fetch response also carries an X-Pm-Gluon-Id header on the message, so message identity inside the server is not limited to the IMAP UIDs.

Where the state lives and how the schema is regenerated

State is kept in a database, and the repository uses ent to generate the code for it. The schema lives in ./internal/db/ent/schema, and the README is explicit that changing it means regenerating the ent code, with the command given as:

bash
pushd ./internal/db/ent && go generate . && popd

The dependency list in go.mod names the pieces that carry this weight: github.com/mattn/go-sqlite3 for the database, github.com/sirupsen/logrus for the logging you see in the demo output, github.com/pierrec/lz4/v4 for compression, golang.org/x/text for text handling, and the emersion IMAP packages, including go-imap and go-imap-uidplus, which is the UIDPLUS extension implementation the capability line advertises. Two testing dependencies are notable: go.uber.org/mock for generated mocks and go.uber.org/goleak for goroutine leak detection, which matters a great deal in a server built around an event loop.

The dependency floor is Go 1.26, and SQLite is C-backed

The go.mod file states go 1.26.1 with toolchain go1.26.7, and it names the toolchain directive explicitly, so a build image with an older Go release will attempt to fetch a newer one rather than fail with a clear message. That is a deployment fact worth planning for, because in a locked-down build environment the toolchain download is exactly the step that tends to fail. The other constraint is mattn/go-sqlite3, which is a cgo binding to the C SQLite library. A pure Go build with CGO_ENABLED unset will not compile it, and a project that insists on a fully static, cgo-free binary will hit that wall. Gluon also depends on bradenaw/juniper, a parser generator, and on the ProtonMail go-mbox package, both of which matter when you compare it to writing an IMAP parser yourself.

The package layout maps the whole server

The directory listing is a readable map of a mail server. imap/ holds the protocol implementation, while rfc5322/, rfc822/, rfcparser/ and rfcvalidation/ deal with message parsing, which is the part of any mail server that is most likely to be wrong when written from scratch. connector/ is the network front, liner/ handles the line protocol, and errors.go sits at the root. Around those sit the operational pieces: async/ and events/ for the event and asynchronous machinery, store/ for persistence, watcher/ for change notification, reporter/ for reporting, observability/ and profiling/ for instrumentation, with the pkg/ and internal/ trees holding shared and private code. The benchmarks/ and tests/ directories, along with a top-level server_test.go, show that the project is developed with measurement and tests in the tree rather than bolted on afterwards.

Where Gluon is the wrong choice

Three situations rule it out. The first is a team that wants mail hosting rather than a mail server library, because Gluon hands you the protocol and the store and leaves delivery, reputation, spam filtering and abuse handling to you entirely. The second is a project that cannot take a cgo dependency, for the SQLite reason described above. The third is a deployment that needs a pinned, supported version, because the repository has no GitHub releases at all and the default branch is dev, so what you vendor today is whatever dev looked like, and the last push was on 2026-10-09. The alternative approach, and the one ProtonMail's own bridge does not offer, is to speak IMAP to an existing server through a client library such as emersion/go-imap, which the dependency list already includes. That approach needs no server code at all and works against any IMAP host, but it gives you a client, not a mail store you can query directly, and it means your application lives inside the protocol instead of owning the data.

Editorial conclusion

Gluon is a good fit for a team that needs to own its mail infrastructure, such as a lab testing mail clients, an appliance vendor embedding IMAP, or an organisation that must keep message state inside its own database rather than behind a vendor API. It is the wrong choice for a team wanting a turnkey mail service, for a project that cannot vendor the C-backed SQLite driver, and for anyone who needs a versioned release to pin against, since the repository publishes no GitHub releases and the default branch is dev. Check three things before building on it: that the go.mod requirement of Go 1.26.1 with toolchain go1.26.7 matches your build image, that the schema generation step in ./internal/db/ent is wired into your build so a schema change does not silently break the store, and that the demo session transcript matches the commands you intend to send, because the capability line advertised at login is the set of extensions you will be held to.

Frequently asked questions

What is Gluon and what does it do?

Gluon is ProtonMail's MIT-licensed Go library for running an IMAP4rev1 server, described as focusing on correctness, stability and performance. The repository has server.go and builder.go at the top level alongside connector/, store/, watcher/ and reporter/ packages, so it is a library you wire into your own binary rather than an installable mail server.

How do I start the Gluon demo server?

Run go run demo/demo.go from the demo directory setup, setting GLUON_LOG_LEVEL=trace for detailed output. The demo server includes two users, both with the password pass, and listens on 127.0.0.1:1143.

Which IMAP extensions does the Gluon demo server advertise?

The login greeting advertises IDLE IMAP4rev1 MOVE UIDPLUS UNSELECT, both at the initial server greeting and in the response to the login command. The session transcript also shows APPENDUID being returned after an append.

What Go version does Gluon require?

The go.mod file declares go 1.26.1 with a toolchain directive of go1.26.7. It also requires github.com/mattn/go-sqlite3, which is a cgo binding to the C SQLite library.

What do I have to do after changing the Gluon database schema?

The README states you must re-generate the ent code after changing the DB schema in ./internal/db/ent/schema, using go generate in the ./internal/db/ent directory.

Official sources

  1. Issues
  2. License: MIT
  3. ProtonMail/gluon on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/protonmail-gluon.svg)](https://hysenlabs.com/projects/protonmail-gluon)

Was this useful?