Documentation

Last updated: 2026-09-12

What happens when your mailbox is a Git repository?

Everlock's mail server is deliberately boring at the edges.

It receives mail over SMTP.

Mail clients submit outgoing messages over SMTP submission.

Apple Mail, Thunderbird, K-9 Mail, and Outlook read mail over IMAP.

There is no Everlock mail client and no special synchronization protocol.

The unusual part begins after SMTP has accepted a message.

Everlock stores mail in the same versioned storage used by the rest of the system.

That means the mailbox is ultimately a Git-backed store.

At first that sounds like an implementation detail.

For mail, it turns out to change several useful properties of the system at once.

The protocols stay ordinary

The first thing worth clarifying is what "mail in Git" does not mean.

SMTP does not become a Git protocol.

IMAP does not expose commits.

A mail client does not know or care that Git exists underneath the server.

From the outside the shape remains familiar:

                    Everlock

incoming SMTP  ---> mail store ---> IMAP
                         ^
                         |
SMTP submission --------+

That separation is important.

Everlock is not trying to replace standard mail protocols with a Git-shaped API.

It uses standard protocols for interaction and Git for durable representation.

Those are different jobs.

The stored message is still an email

A received message is stored as a raw .eml file.

Everlock keeps its own mailbox metadata next to it in a .toml sidecar.

A mailbox can look roughly like this:

data/everlock-mail/
  rules.toml
  domains/
    example.com.toml
  example.com/
    alice/
      mailbox.toml
      2026/
        05/
          msg-1.eml
          msg-1.toml
          msg-2.eml
          msg-2.toml

That is one of the properties I like most about the design.

The application does not turn an email into an opaque database record that only the mail server knows how to reconstruct.

The original message remains an ordinary mail message.

Everlock-specific state lives next to it rather than replacing it.

The repository contains both content and mailbox state

Mail is more than a directory of .eml files.

IMAP has state.

Messages need UIDs.

A mailbox has UIDVALIDITY.

Messages have flags and folder membership.

Clients expect changes to those values to behave consistently.

Everlock keeps mailbox-level state in:

mailbox.toml

including:

next_uid
uidvalidity

The UID is also encoded into the message filename, so an operation such as:

UID FETCH 42

can resolve directly to the corresponding stored path instead of searching a separate database index for the primary identity.

The important part is that this state participates in the same versioned store as the messages themselves.

Folders are metadata, not directories

One design choice I find particularly useful is that IMAP folders are not represented by moving messages through a directory hierarchy.

A message does not need to physically move from:

inbox/

to:

archive/

Instead, the folder lives in the message sidecar.

Conceptually:

folder = "inbox"

can become:

folder = "archive"

The raw .eml does not have to change.

That separates two things that mail clients often present as one operation:

message content
mailbox organization

The first can stay immutable.

The second can change as metadata.

That is a comfortable fit for Git.

SMTP and IMAP share the same durable state

Everlock splits the implementation into SMTP and IMAP pieces, but they share the same versioned mail store, normally everlock-mail.

SMTP writes messages into it.

IMAP reads and mutates mailbox state in it.

A MailEventBus handles the immediate runtime notification path so an IMAP client can see newly delivered mail without waiting for a repository poll.

That gives two layers:

durable state:
Git-backed mail store

live notification:
MailEventBus

This is another useful separation.

Git does not have to become the real-time event system just because it is the durable storage system.

A new message can be committed once, while IMAP IDLE receives an in-process notification that something changed.

One IMAP command becomes one storage commit

IMAP mutations can touch several pieces of mailbox state.

A command might alter flags.

A move may change message metadata and mailbox counters.

An expunge may remove message state while preserving UID rules.

If those pieces were written independently, clients could observe an intermediate mailbox state that never really existed from the user's perspective.

Everlock therefore treats one IMAP command as one versioned-storage commit.

Conceptually:

mailbox state A
      |
      | IMAP command
      v
mailbox state B

The command is the application-level transaction boundary.

Git is not pretending to provide arbitrary SQL transactions.

It is giving the mail backend an atomic version boundary for the set of file changes that make up one mailbox operation.

Mail is unusually compatible with immutable history

A lot of application data fights Git.

Mail is different.

Incoming mail is naturally append-oriented.

A new message arrives.

The old messages do not need to be rewritten.

The raw message itself is effectively immutable after receipt.

Most later changes concern metadata:

seen
answered
flagged
folder
deleted

That means a large part of the workload already has the shape Git likes:

existing objects remain
new state refers to new or changed metadata
history accumulates

This is one reason "Everything Is Git" feels less artificial for mail than it might first sound.

Mail already behaves a lot like an append-only archive with mutable annotations.

Deletion becomes a versioned state change

In ordinary application storage, deletion often means:

row gone
file gone

In Everlock's versioned storage, deleting a path is itself a commit.

So there is a distinction between:

not present in the current mailbox

and:

never existed

That distinction is exactly what versioned storage is meant to preserve.

Mail operations such as EXPUNGE and MOVE still have normal IMAP semantics from the client's point of view.

Underneath, the resulting durable state is another version of the repository.

The current tree represents what the mailbox looks like now.

Older commits represent what it looked like before.

Actual removal of old historical objects is a separate retention question.

That is healthier than pretending a user-facing delete button and physical destruction of every historical copy are the same operation.

Backup no longer needs to understand mail

Traditional mail systems can involve several storage concerns at once.

Raw messages may live in one layout.

Indexes somewhere else.

Account state elsewhere.

Rules in configuration files.

A database may contain additional metadata.

A backup system then has to know which pieces are authoritative and how to capture them consistently.

Everlock has a much simpler durable boundary:

everlock-mail Git store

That repository contains the raw messages, their sidecars, mailbox state, mail-domain configuration, and rules belonging to the mail store.

The backup system does not need to understand IMAP.

It does not need to reconstruct messages through an export API.

It protects the repository.

That is the same backup story Everlock uses elsewhere.

A clone is meaningful without the mail server

This property matters more than "backup is easy."

The versioned store is a standard bare Git repository.

With the appropriate grant, it can be cloned through Everlock's normal Git service and inspected using standard Git tooling.

The important part is what is inside the clone.

Not a proprietary mail database.

Not serialized Rust structs that require Everlock to interpret them.

You get files such as:

msg-1.eml
msg-1.toml
mailbox.toml
rules.toml

The .eml is still an email message.

Another mail parser can read it.

A text editor can inspect it.

Git can show when it entered the repository and how the surrounding metadata changed.

Everlock is useful for serving the mailbox.

It is not required merely to prove the messages exist.

This changes what "owning your mail" means

Self-hosting usually answers:

Where does the server run?

That is useful, but incomplete.

A self-hosted server can still keep its most important state in an application-private database that only that application understands.

Then ownership still depends on keeping the software operational.

The Git-backed model tries to make a stronger promise:

The durable representation remains useful even if Everlock is not running.

For mail, that means the thing you care about most, the messages themselves, are already sitting in ordinary files inside a standard repository.

There is no special "export my mailbox before I stop using this product" step required to get a durable copy.

History becomes an audit trail almost accidentally

Because writes are commits, mail storage naturally accumulates a history.

A delivery changes the repository.

An IMAP mutation changes the repository.

Configuration changes change the repository.

The commit log can therefore describe the progression of mailbox state.

This is not a replacement for SMTP access logs or security audit events.

Those have different granularity and different semantics.

But it does mean the durable mail state has a history without the mail backend maintaining a second history table.

That is one of the recurring benefits of Everlock's storage model:

current data
and
history of that data

are not separate persistence systems.

The original message remains the authoritative object

The sidecar design also gives the system a useful hierarchy.

The .eml is the original message.

The .toml contains Everlock's interpretation and mailbox state.

That makes it easier to reason about what can be rebuilt or changed later.

For example:

raw message          authoritative content
mailbox metadata     application state
client-side indexes  rebuildable

Everlock currently does not provide server-side IMAP SEARCH; clients fall back to local indexing.

That limitation is interesting architecturally.

There is no hidden search database whose state must be treated as part of the authoritative mailbox.

If a future server-side index is added, it can remain a derived structure as long as it can be rebuilt from the repository.

The Git store remains the source of truth.

Standard protocols make the storage model disappear

From Apple Mail, none of this is visible.

That is exactly right.

The client sees:

IMAPS
SMTP submission
standard folders
UIDs
IDLE notifications

The user does not need to understand Git to read an email.

The Git model becomes visible when something operational happens:

backup
migration
inspection
recovery
audit
history

That is where the storage representation matters.

This is a pattern I increasingly like for Everlock:

Standard protocols on the outside, durable Git representation on the inside.

The outside remains compatible.

The inside remains portable.

Git does not solve mail delivery

It is worth keeping the claim narrow.

Git does not make SMTP easy.

It does not solve SPF.

It does not solve DKIM.

It does not solve DMARC.

It does not run an outbound retry queue.

It does not replace IMAP's state machine.

It does not make mail abuse or deliverability disappear.

Those are mail problems, and Everlock still has to implement them as mail problems.

The benefit appears at the persistence boundary.

Once Everlock has decided:

this message belongs in this mailbox

the resulting durable state does not need a separate mail-specific database architecture.

Git also does not need to be the query engine

The same restraint applies in the other direction.

A Git repository is not a good general mail search engine.

If Everlock later adds server-side SEARCH, it can build an index optimized for that purpose.

The important rule is that the index should be disposable.

If it disappears, the repository still contains enough information to rebuild it.

That keeps the architecture honest:

Git store
    |
    +--> authoritative mail
    |
    +--> rebuildable search index

rather than quietly creating another database whose loss makes the mailbox incomplete.

Mail exposes why the model is useful

With something like static site content, putting files in Git feels obvious.

Mail is more interesting because users normally think of it as a server application rather than a repository.

Yet the underlying data has a surprisingly compatible shape:

messages are append-heavy
raw content is naturally immutable
foldering is metadata
mailbox mutations have meaningful transaction boundaries
history is valuable
backup matters enormously

That makes mail a good test of the "Everything Is Git" idea.

If the model only worked for Markdown and configuration files, it would not be much of an architectural principle.

Mail shows that the same storage primitive can sit underneath a stateful standard protocol without forcing Git into the protocol itself.

The mail server can disappear before the mail does

That is ultimately the property I care about.

Everlock can stop.

The IMAP frontend can be replaced.

The SMTP implementation can change.

Search can be added or rebuilt.

The UI can eventually become something completely different.

The durable representation remains:

Git repository
  |
  +--> raw .eml messages
  +--> explicit mailbox metadata
  +--> history

That changes the relationship between the application and the data.

The mail server becomes the thing that serves the mailbox.

It is no longer the only thing capable of explaining what the mailbox contains.

And for a system built around the idea that your data should outlive the software managing it, mail may be one of the clearest examples of why that matters.

For implementation details, see Mail backend and Versioned storage.

updates mail git imap smtp storage design