lilmail
GitHub
v1.13.0MIT or Apache-2.0

Your mailbox, your machine, one small binary.

A self-hostable PIM client — mail, calendar and contacts — that talks to the IMAP/SMTP, CalDAV and CardDAV account you already own. Server-rendered HTML with every asset compiled into one Go binary: no build step, no CDN, no database to provision, and no service in the middle.

lilmail with a message open in the three-pane reading view lilmail in dark mode with a message open in the three-pane reading view
no database · no CDN · no build step
01

Everything a mail client needs. Nothing it doesn't.

Core mail is always on. Everything past it is a config section you opt into — and costs nothing while it's off.

One process, one file on disk, one thing to back up. lilmail is deliberately small: it doesn't index your mail into a second database, it doesn't run a queue, and it doesn't need a build step to serve a page.

8 subsystems · 1 binary · 0 services to run
01

One binary, no database

Templates and vendored JS compiled in with embed.FS; durable state is an embedded bbolt file. An optional Postgres backend covers shared or multi-instance deploys. Runs air-gapped with only a config.toml.

02

Mail that just works

IMAP browsing and SMTP sending, conversation threading via the JWZ algorithm, plain-text and rich-text compose, attachments with inline cid: images, scheduled send, and drafts that auto-save every 30 seconds.

03

Sign in your way

OAuth2 / OpenID Connect authorization-code flow with PKCE (S256), automatic refresh-token handling, and XOAUTH2 / OAUTHBEARER SASL — with classic password login still working alongside it.

04

Calendar & contacts

CalDAV month and week views with event CRUD, free/busy, and end-to-end iTIP/iMIP meeting invites. CardDAV address-book lookup powers recipient autocomplete.

[caldav] · [carddav]
05

Real-time notifications

An IMAP IDLE watcher feeding an SSE stream, browser notifications, native desktop toasts, and VAPID Web Push — so a new message lands without a refresh.

[notifications]
06

An AI assistant, if you want one

Smart compose, thread summaries, reply suggestions, action-item extraction and phishing detection — through any OpenAI-compatible endpoint you point it at, including one on your own machine.

[ai]
07

A stable /v1 JSON API

Folders, paginated messages, search, flags, move/archive/spam, snooze, compose and drafts, attachments, calendar, contacts and settings — the same engine and session auth that serve the HTMX UI.

08

Security you don't configure

JWT sessions, AES-256-GCM encrypted credentials at rest, a strict Content-Security-Policy, SameSite=Lax cookies, a sandboxed mail iframe, and CR/LF/NUL header-injection guards on everything outgoing.

02

No one in the middle. Not even us.

lilmail is a client, not a service. It runs on your machine and speaks straight to the mail server you already have — no lilmail account, no relay, nothing phoning home.

You Your browser Server-rendered HTML over HTMX — or any client on /v1 Your server lilmail one Go binary + a config.toml bbolt on disk · optional Postgres Your accounts IMAP · SMTP CalDAV · CardDAV, on Gmail, Outlook or any standards server HTTP TLS · OAuth
YouYour browser

Server-rendered HTML over HTMX — or any client on /v1.

Your serverlilmail

One Go binary and a config.toml. bbolt on disk, or your own Postgres.

Your accountsIMAP · SMTP · DAV

Gmail, Outlook, or any standards-compliant server, over OAuth or password.

a lilmail relay— does not exist. There is no hosted lilmail to sign up for, and no key we hold.

03

Warm, quiet, and quick.

A hand-written design system — no framework, no utility soup — with a real dark mode. Every screen here is a live capture from the repo's own Playwright script.

Inbox

Three panes, no chrome in the way

Folders, a threaded message list and the reading pane — with unread dots, attachment markers and per-sender avatars. HTMX swaps the panels; the page never reloads.

Reading

HTML mail, safely contained

Messages render inside a sandboxed iframe under a strict CSP. Reply, forward, archive, flag, print and delete sit in one row where you'd expect them.

Compose

Write, attach, schedule

Plain-text or rich-text, file attachments as proper multipart MIME with inline cid: images, send-later scheduling, and drafts auto-saved every 30 seconds.

Search

Straight to the server

Search is executed by your IMAP server, not by a local index that has to be built and kept fresh. The same query is a GET /v1/search away.

Settings

Signatures, identities, away messages

Send-as identities, signatures, the vacation responder, notification keys and connected accounts — each one also reachable over /v1/settings.

Sign in

Your address, your password — or your IdP

Email and mail-server password by default. Configure [oauth2] and the sign-in button runs a full authorization-code flow with PKCE.

The lilmail inbox The lilmail inbox in dark mode A message open in lilmail A message open in lilmail, dark mode The lilmail compose window The lilmail compose window in dark mode Search results in lilmail Search results in lilmail, dark mode The lilmail settings screen The lilmail settings screen in dark mode The lilmail sign-in screen The lilmail sign-in screen in dark mode
Inbox1280 × 800 · live capture
04

Running in about a minute.

Nothing to sign up for and nothing to provision. Point lilmail at the mailbox you already have, and open it.

~/ — build and run
git clone https://github.com/vul-os/lilmail.git
cd lilmail

# copy the annotated example, then fill in your server
cp config.toml.example config.toml

go run main.go        # or: make build && ./lilmail
# → http://localhost:3000
config.toml — the whole minimum
[server]
port = 3000

[imap]
server = "imap.example.com"
port   = 993
tls    = true

[smtp]
port         = 587
use_starttls = true

[jwt]
secret = "change-me-to-a-long-random-string"

[encryption]
key = "a-32-character-encryption-key!!"
  1. 1
    Get the binary

    Build it with Go 1.23+, pull a release archive, or run the container. Only config.toml has to sit alongside it.

  2. 2
    Point it at your mailbox

    The minimum is an IMAP server, a JWT secret and a 32-character encryption key. Every optional section stays off until you add it.

  3. 3
    Sign in

    Open http://localhost:3000 and use your email and mail-server password — or the OAuth2 button once [oauth2] is configured.

Generate real secrets before deploying: openssl rand -hex 32 for jwt.secret, and a random 32-character encryption.key — it's the AES-256 key protecting stored credentials.
05

One engine, two front doors.

The HTMX UI and the /v1 JSON API are the same mail engine under the same session auth. Build a rich client, a script or a widget on it — the shapes are a stable, additive contract.

GET/v1/folders — mailboxes and labels
GET/v1/messages — paginated, per folder
GET/v1/messages/:uid — full message + parts
GET/v1/search — server-side IMAP search
PATCH/v1/messages/:uid/flags — read, flagged, …
POST/v1/messages/:uid/move — archive, spam, folder
POST/v1/messages — send now, or sendAt
POST/v1/attachments — upload, then reference
GET/v1/calendar/events — CalDAV, when enabled
GET/v1/contacts — CardDAV, when enabled
the first page of the inbox
curl -s --cookie jar.txt \
  'http://localhost:3000/v1/messages?folder=INBOX&limit=25' \
  | jq '.messages[0] | {from, subject, date}'

The API reuses the session cookie from POST /login or the OAuth2 flow — there's no second token scheme to manage. Unauthenticated calls answer 401 with JSON instead of redirecting, so a fetch() client can react in code.

Full API reference
06

Independent, all the way down.

Think Evolution plus Evolution-Data-Server, for the web: a fully independent project with no hosted service behind it and nothing it needs from anyone else's infrastructure.

Your own accounts

lilmail connects to the mailbox you already own — Gmail, Outlook or any standards-compliant server — over OAuth or password.

  • No lilmail account to create
  • No relay handling your mail
  • Multi-account with per-account error isolation

No external services

Frontend assets are embedded with embed.FS and durable state is a local bbolt file, so an offline box is a supported deployment.

  • No CDN, no asset pipeline
  • No database server to provision
  • Linux, macOS and Windows builds

Security-first

Sensible defaults rather than switches you have to remember, and a threat model written down in the docs.

  • JWT sessions; AES-256-GCM credentials at rest
  • Strict CSP and SameSite=Lax cookies
  • Sandboxed mail iframe; header-injection guards
Vulos

Part of Vulos

Vulos is a family of open, self-hostable apps. lilmail is a fully standalone product — it never requires Vulos infrastructure — and its /v1 API is the seam Vulos OS and any other rich client integrate over.

Explore Vulos