← All writing

From Lumimail to Picket

How my Lumimail fork became Picket, from connected mailboxes and a new identity to import recovery, Mail insights, and configurable categorization.

Picket started as my fork of Lumimail, an open-source mail application built around Cloudflare. That starting point deserves credit: Lumimail supplied the foundation for hosting mail and reading it in a web interface.

Since then, my fork has grown into a workspace for hosted and connected mailboxes, with changes to reading, access control, recovery, deployment, and presentation. The latest work adds Mail insights and configurable categorization: tools for understanding an accumulated inbox and deciding how it should be organized.

The visible name became Picket in September. The repository is still called lumimail, so both names appear in the project's history.

Building a workspace around the mailbox

Some of the changes are easiest to notice while reading mail. Desktop split view keeps the message list beside a reading pane, with adjustable sizes and a choice of orientation. On smaller screens, opening a message still uses a dedicated page. Unified settings bring related controls together, while mailbox selection keeps the working scope visible.

Shared access also needs more detail than a single account role. Picket separates organization roles from permissions on individual mailboxes. A person can have permission to view a mailbox, respond from it, or manage it. Those distinctions affect which messages and controls the application exposes.

Behind the interface, the fork gained signed release artifacts, explicit production promotion, and operational views for inspecting mail flow, queues, and recovery evidence. Those are part of maintaining a mail application: when delivery or processing stops, the operator needs enough information to identify the failed step.

The application continues to use Next.js and React on Cloudflare Workers, with D1 for structured data, R2 for stored message content and attachments, and queues for asynchronous processing.

Bringing existing accounts into Picket

External accounts widened the project beyond mail hosted on a configured domain. Picket has delegated account connections for Google and Microsoft, allowing mail to be imported and messages to be sent through the connected provider.

Connecting an existing mailbox introduces a different workload from receiving new mail. An import can run for a long time, encounter an unusual message, lose a worker midway through a page, or need fresh authorization.

The import records progress in bounded jobs and checkpoints. Provider message identifiers make replay safe from duplicate imports. Account owners have controls to pause, reconnect, disconnect, and retry.

The integration also has a defined boundary. Local labels and read-state changes do not become matching Gmail labels or folders. Optional retention stores an exact original message in R2 after consent; that retained copy has a narrower purpose than a complete mailbox backup and restore system.

Giving Picket its own identity

The September rebrand covered the places where an application announces its name: the web interface, authentication screens, installed-app metadata, offline assets, notifications, and transactional messages.

The visual work used the Mantle design package. Picket adopted its navy, amber, and teal palette, Plus Jakarta Sans headings, IBM Plex Sans body text, and shield-and-route mark. Dense mail screens keep decorative motifs out of the reading area, while landing and authentication screens have more room for them.

One small detail required care: the supplied mark's silhouette. The application assets preserve the original geometry through crops and masks, rather than approximating it with a fresh drawing.

The rename also had to preserve existing installations. Internal storage keys, resource identifiers, queue names, and other compatibility-sensitive names stayed stable. Picket could acquire a visible identity while continuing to use the existing mail and infrastructure.

Making imports recover and explain themselves

The latest changes merged into the public repository, on October 1, concentrated on external-account recovery.

A failing message could otherwise prevent later messages from being processed. The recovery work distinguishes a repeatedly failing individual message from a wider processing failure. After repeated message-specific failures, the importer records the skipped item and continues. Oversized messages also receive a recorded skip reason.

A broader failure needs a different response. A database outage, for example, should not cause a run of messages to be treated as individually broken. Systemic failures have bounded retries and backoff, while authentication loss asks the owner to reconnect.

Owner-facing banners make the resulting state visible: a retry is scheduled, reconnection is needed, retries are exhausted, or messages were skipped. Manual retry is available in recoverable error states.

The retry and failure logging, automatic recovery, and owner attention banners changes belong together. Recovery needs both a processing policy and an explanation the mailbox owner can act on.

The newest work is understanding the imported mail

Mail insights is the next stage of that work. Once an existing mailbox is imported, the application needs ways to inspect its contents and apply useful categories.

The overview supports 7-, 30-, and 90-day windows, with received volume, sender groupings, tag counts, and recent examples. Sender analysis can become a starting point for an exact-sender rule. The mailbox selection scopes the insights, sample comparisons, and historical processing.

Categorization combines local rules with optional AI questions. Local rules can match a sender, domain, subject text, or body text. They work without an AI provider.

For mail that needs interpretation, the question editor supports configurable choices, yes-or-no answers, and ordered scores. Starter questions cover message type, whether a reply is needed, and urgency. Definitions, label mappings, and thresholds can be edited, so the classification policy is visible in the interface.

For example, a newsletter may contain a request to click a link. The reply question's definition excludes automated marketing calls to action. That distinction is something the question should express explicitly.

Trying a policy before applying it

The latest workflow lets a user compare the current and proposed configuration on a small sample before activation. Editing the proposal makes the previous sample stale.

Activation saves an immutable configuration version. Historical processing uses a resumable job tied to that version, with progress, retry, and cancellation. Each batch processes five messages, and scheduled processing can continue after the browser closes.

Personal corrections persist when messages are processed again. They change the person's categorization of that message; they do not train the external model.

This categorization workflow applies personal labels. It does not automatically archive, delete, mark messages as read, or move them to Spam. Optional AI evaluation requires consent and sends bounded message text to TypeSafe AI, also called Jev in the implementation. Attachments and full threads are excluded.

As of October 19, the Mail insights interface is live on Picket. This newest development extends beyond the October 1 public repository head. Classification quality still needs to be judged against the mail being processed; a visible label and an editable question make those decisions available for review.