Build Mimestream
KINDAreplaces $4.99/mosaves $59.88/yrback to the verdict
A native client over an existing mailbox that syncs incrementally rather than refetching, stores messages locally so search and scrolling are instant, models labels honestly including a message in several at once, and sends through the account's normal service so deliverability stays someone else's problem.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree if you own one
Why The reason to build this is native performance, which exists nowhere else.
Get it Apple menu, then About This Mac.
Verify
About This Mac reports macOS 13 or higher - installfree
Why Builds the client and provides Instruments for the list and search work.
Get it Mac App Store or developer.apple.com/xcode. open ↗
Verify
xcodebuild -version prints 16 or higher - API keyfree for personal use; verification may be required for wider distribution
Why Without them the client cannot connect to a real mailbox, and a test mailbox does not have the label complexity that matters.
Get it Create a project in the provider's developer console, enable the mail scopes you need, and configure the redirect. Some providers review applications requesting broad mail access, which takes time.
Verify
The flow completes and returns a token - have readyfree
Why Sync, search and list performance are all trivial on a small mailbox and decisive on a real one, and label complexity only appears in mail people actually use.
Get it Your own account. If it is small, generate a large fixture as well, but test against the real one for label behaviour.
Verify
The account connects and reports its message count - account99 US dollars per year
Why Product phase only. Without it there is no notarization and a warning on other machines.
Get it Enrol at developer.apple.com/programs. open ↗
Verify
A Developer ID Application certificate appears in Xcode settings
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- accounts(id, address, token_ref, history_id, last_synced_at) - messages(id, account_id, provider_id, thread_id, from_addr, to_json, subject, snippet, received_at, size, raw_key) - labels(id, account_id, provider_id, name, kind), message_labels(message_id, label_id) · many to many, which is the point - threads(id, account_id, provider_id, subject, last_message_at, message_count) - messages_fts · full text over subject and body - pending_actions(id, kind, message_id, label_id, created_at, attempts, last_error) · the outbox that makes offline actions safe - A local action is applied immediately and queued for the server. The queue is what makes the client feel instant without lying.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ~/Library/Application Support/Mail/mail.db | Local message cache. Contains your mail, so treat the file and its backups accordingly. |
OAUTH_CLIENT_ID | required | replace-with-client-id | From the provider's developer console. |
INITIAL_SYNC_DAYS | required | 90 | How much mail to fetch before the client becomes usable. Older mail backfills after. |
MAX_RAW_CACHE_GB | required | 10 | Cap on stored raw messages. Oldest are evicted and refetched on demand. |
ACTION_MAX_ATTEMPTS | required | 6 | Retries before a queued action is surfaced as failed. |
The build, in order
Authorising without touching a password
The account is connected through its own authorisation flow and the token is in the keychain.
Narrowest scopes. Add scopes when a feature needs one, not in advance.
Files
Auth.swiftReferenced from the database, never stored in it. Inspect the file to confirm.
Files
Keychain.swiftA clear reconnect prompt, local data intact.
Files
Auth.swift
done when · tick each as it passeswatch out- Requesting broad scopes because it is simpler means the consent screen asks for more than you need and users refuse, and it means a token leak is worse than it had to be. Request the narrowest scopes and add one only when a feature actually needs it.
Incremental sync
A mailbox with a hundred thousand messages syncs changes rather than refetching.
Recent mail first so the client is usable in a minute, older mail backfilled after.
Files
Sync.swiftPull by history point. Store the point after each successful batch, not at the end.
Files
Sync.swiftFull resynchronisation, tested by corrupting the stored point deliberately.
Files
Sync.swift
done when · tick each as it passeswatch out- A change cursor expires after a period, and a client that treats an expired cursor as an error stops syncing silently until someone notices missing mail. Detect the specific response and fall back to a full resynchronisation rather than failing.
Labels modelled as labels
A message in three labels appears in all three, and archiving removes one rather than moving anything.
A join table. Not a folder column with a favourite label.
Files
Labels.swiftInbox is a label. Archiving removes it and nothing else.
Files
Actions.swiftRather than choosing one. Users know their messages are in several places.
Files
MessageView.swift
done when · tick each as it passeswatch out- Mapping labels to a single folder column forces a choice about which label a message lives in, and every subsequent behaviour is wrong: archiving from one view removes it from another, and a message in two labels appears in one. This is the modelling decision that cannot be corrected later without a rewrite.
Instant search and scrolling
Searching a hundred thousand messages is faster than the web client.
A hundred thousand messages. Every performance claim depends on it existing first.
Files
Fixtures.swiftFull text over subject and body, updated as messages sync.
Files
Search.swiftRender rows, load a window. Never the whole label.
Files
ThreadList.swift
done when · tick each as it passeswatch out- Loading a whole thread list into memory works until a label has thirty thousand messages, and then the client stalls on opening it. Page the list and render rows, which is what a native list view is for and the reason to build this natively at all.
Actions that work before the network does
Archiving feels instant and is never lost, even offline.
The view updates at once; the queue makes it true.
Files
Actions.swiftThen surface the failure. A silently dropped action is the worst outcome.
Files
Outbox.swiftServer wins, user is told. Silent reversion looks like a bug and destroys trust.
Files
Reconcile.swift
done when · tick each as it passeswatch out- Applying locally and never reconciling means a failed action leaves the client showing a state the server does not have, and the message reappears days later with no explanation. Queue with retries, reconcile against the server on the next sync, and surface a conflict rather than flipping the message back silently.
Composing, sending and shipping itproduct builder
You can reply correctly, and someone else can install it.
The identifier chain on every reply. Verify in a different client rather than your own.
Files
Compose.swiftSo deliverability and sent items both behave normally.
Files
Send.swiftWhat was requested and what it is used for. Mail access deserves a plain statement.
Files
README.mdterminalxcrun notarytool submit Mail.zip --keychain-profile AC --wait
done when · tick each as it passeswatch out- Dropping the reply headers breaks threading in every other client the recipient might use, and it looks like a new conversation to them. Preserve the message identifier chain on every reply, and verify by replying to yourself and checking the thread in a different client.
That is the whole plan for Mimestream. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Deep provider-specific feature support.
- Mail hosting and deliverability.
- Push notifications across platforms.
- Support for other mail providers.
- deep Gmail API use, native macOS polish, sync reliability, and support
- mail hosting and deliverability
- push on every platform
- advanced team collaboration
- provider-specific AI and search
- Scheduled send, which is local queuing you already have
- Rules applied locally on arrival, since the index is already there
- A second account, which mostly works if the model stayed account-scoped
Need the files? The project pack on the verdict page hands your agent the whole brief · more email.