JAMES: Internal Documents

Architecture

Legacy email infrastructure

Legacy email infrastructure

A legacy mail infrastructure is composed of…

JMAP infrastructure

JMAP infrastructure

JMAP is a new protocol unifying MTA and MDA responsibilities. HTTP and JSON based, stateless, the user webmail can directly communicate with the mail server.

Application context

Application context

Here are the interactions happening with the email server system:

Software components

Software components

James lies at the heart of our infrastructure both as a MTA and MDA. HTTP load balancing can be set up for JMAP and webadmin (stateless), however statefull protocols like SMTP and IMAP requires TCP session load balancing.

We rely on two postfix (in & out) as an MX because James mailqueue elacks required capabilities namely delays.

Regarding databases:

Optional dependencies includes:

Transfer of events and contacts to Sabre (calendar) is done through a RabbitMQ exchange.

Calls to LinShare are done through the APIs

James Components

James components

James leverages protocol stacks: IMAP, JMAP, SMTP, WebAdmin.

The MailQueue ensures an asynchronous processing of submitted emails and sits between email submission ie via SMTP.

The Mailbox stores users email and folders. It is accessed via MDA protocols like IMAP or JMAP.

MailProcessing logic is fully configurable based on standard components.

Extending James behaviour

Extensions

Several extension mechanisms exists enables to extend James server without recompiling the all source code:

Component details

Mailbox component

Mailbox component

The various components interacts with the mailbox through the mailbox-api.

These components ensures the operation matches the access rights by checking the quota manager, as well as quota policy.

All events are fired on the eventBus, which then triggers notifications, and extra functionalities listed on the diagram. Errors are retried and when a trheshold is crossed, events are stored on dead-letter for admin diagnostic as well as later replay.

Search is conducted by ElasticSearch, data is asynchronously indexed via the eventBus.

Mail processing component

Mail processing component

Mail processing allows to take asynchronously business decisions on received emails.

Here are its components:

This diagram presents a few mailets interacting with core components:

MailQueue component

Mailqueue component

An email queue is a mandatory component of SMTP servers. It is a system that creates a queue of emails that are waiting to be processed for delivery. Email queuing is a form of Message Queuing – an asynchronous service-to-service communication. A message queue is meant to decouple a producing process from a consuming one. An email queue decouples email reception from email processing. It allows them to communicate without being connected. As such, the queued emails wait for processing until the recipient is available to receive them. As James is an Email Server, it also supports mail queue as well.

Why Mail Queue is necessary

You might often need to check mail queue to make sure all emails are delivered properly. At first, you need to know why email queues get clogged. Here are the two core reasons for that:

Some mailbox providers enforce email rate limits on IP addresses. The limits are based on the sender reputation. If you exceeded this rate and queued too many emails, the delivery speed will decrease.

Another common reason is that your email has been busted by spam filters. The filters will let the emails gradually pass to analyze how the rest of the recipients react to the message. If there is slow progress, it’s okay. Your email campaign is being observed and assessed. If it’s stuck, there could be different reasons including the blockage of your IP address.

Why combining Cassandra, RabbitMQ and Object storage for MailQueue

Enqueues

Upon enqueue the payload is saved to the objectStorage, metadata saved in the mail queue view, then fired in RabbitMQ.

Upon dequeue, when a rabbitMQ event arrives, the payload is retrieved from the object store, then the view checked to see if it had been deleted.

The view is composed of a read only time serie, with deletions stored into a separated table. It enables reading and deleting queue content. A pointer, the browse start, enables to skip most deleted data of the time serie. Browse start is probabilistically updated upon dequeue and deletes.

Monitoring

Logging stack

James logs at a JSON format to its standard output and fluentbit forwards logs to an ElasticSearch server, for a later display in Kibana.

James logs includes structured information (eg user, ip, session ids, etc…)

Metrics

JAMES outputs its metrics directly in ElasticSearch for a later display in Grafana.

Upcoming work will let James expose its metics over HTTP for a later collect via Prometheus and a display in Grafana (WIP).

We also have set up for the Glowroot APM.

BlobStore

BlobStore component

We rely on the blob store to store our binary data.

This includes:

Our blob storage relies on the following technologies: