MailFerry v2.1.0 — Feature Release
High-Performance Native IMAP Migration Engine.
OAuth 2.0 / modern authentication with a first-class mailferry auth
lifecycle, IMAP MULTIAPPEND batching, evidence-based server
identification, and a substantially polished operator experience.
Migration correctness, the statistics identity, terminal sanitisation
and SIGHUP handling are unchanged and still test-enforced. Existing
password migrations and existing mailferry.db / mailferry.toml
files continue to work untouched.
Added
- OAuth 2.0 / modern authentication (SASL XOAUTH2, and
OAUTHBEARER for standards-compliant servers). First-class
Microsoft 365 / Exchange Online and Google Workspace / Gmail,
plus a generic provider. Source and destination authenticate
independently — password↔OAuth, or two different providers, in one
run. The canonical 10-column CSV is unchanged: an endpoint opts in
with anoauth:<profile>password cell. - Three authentication models, kept deliberately distinct:
delegated(one user's own mailbox),application(Microsoft 365
app-only,IMAP.AccessAsApp— one tenant app → many mailboxes) and
service_account(Google domain-wide delegation → many mailboxes).
An administrative profile mints its tenant token once and shares
it across every mailbox and worker in the run. mailferry authcommand family —add/list/show/
test/refresh/remove. Browser loopback + PKCE (RFC 8252),
headless device code (RFC 8628) for Microsoft, and manual redirect
capture for Google.--usernames the mailbox you mean: it is sent
as the OIDClogin_hintand the account that actually signed in is
verified afterwards — on a mismatch nothing is saved.
auth testis a staged diagnostic: token → TLS → SASL →LIST,
then the server's full capability list and a plain-language verdict.- Secrets are never stored in configuration. Client secrets,
refresh tokens and service-account keys are held as external
references (env:/file:/keychain:/dpapi:/cmd:) and
resolved at run time — never written tomailferry.toml, the State
Database, logs, reports or traces. Storage uses the OS-native secure
store: macOS login keychain, Linux Secret Service, Windows DPAPI
(current-user scope), with a documented0600fallback. - IMAP MULTIAPPEND (RFC 3502) — several messages uploaded in one
APPEND command when the destination advertises it. On by default
with automatic batch sizing (--multiappend auto|off,
--multiappend-batch-size auto|N). On the built-in benchmark, 400
messages went from 400 append commands to 50. It is an
optimisation and nothing more: bodies still stream (memory stays
proportional to a chunk, not a batch), an unsupported destination
transparently uses one append per message, and every message keeps
its own durable state, destination UID, flags and INTERNALDATE.
Batch size is not concurrency. - Evidence-based server identification. MailFerry reports what the
server positively says about itself — OAuth provider, RFC 2971 IMAP
ID, vendor-private CAPABILITY, greeting banner, TLS certificate —
and displaysUnknownwhen the evidence is insufficient. There is
no hostname table, provider list or fingerprint database, and the
identifier is never given the hostname, so guessing from a domain is
structurally impossible. - Adaptive Dashboard. A Source / Destination panel reporting host,
server, auth, negotiated TLS and that connection's current speed;
for heterogeneous (MSP) runs it aggregates by identified platform
rather than showing one mailbox as if it spoke for the run. Plus a
live F3 mailbox inspector, one unified quit confirmation (Esc
never exits), a Runtime that freezes on completion, and a single
authoritative completion timestamp. - Interactive F9 Settings and an expanded typed configuration
surface, saved atomically tomailferry.tomlwith comments
preserved. - Documentation:
docs/OAUTH.md(the authentication model),
docs/OAUTH-SETUP.md(step-by-step Microsoft Entra and Google Cloud
configuration, verified against the official provider documentation
on 01/08/2026) anddocs/MULTIAPPEND.md.
Fixed
- Cancelling a run no longer abandons appends already on the wire.
A stopped transfer settles the commands it has already sent, so each
message's outcome is recorded rather than left ambiguous — removing
a path where a resume could copy a message twice. - A starting migration could fail if something was watching it.
SQLite does not applybusy_timeouttoPRAGMA journal_mode, so a
read-only observer (mailferry attach) opening the file during the
initial WAL switch could abort the run withdatabase is locked.
Schema initialisation now retries on lock contention. keychain:secret references are now resolvable on Linux (Secret
Service viasecret-tool); previously a reference could be stored
that only macOS could read back.
Verification
gofmt, go vet (darwin/linux/windows), the full unit and end-to-end
suites and the race detector all pass. Two clean builds of this commit
are byte-identical.
Known limitations
- macOS binaries are not yet Apple-notarised — Gatekeeper may block
the first launch and a one-time Open Anyway is expected. See
docs/INSTALLATION-MACOS.md;
never disable Gatekeeper. The signing pipeline is documented in
docs/RELEASING-MACOS.mdand planned for a future release. - Terminal/PTY regression tests are Linux-only and were not executed on
the macOS build host. - Real-provider verification to date covers the Microsoft 365
delegated browser flow. The device-code, app-only, Google
delegated and Google domain-wide-delegation paths, Windows DPAPI
storage and Linux Secret Service are implemented and unit-tested
against mocked providers, but have not yet been exercised against
live tenants.
Verifying your download
shasum -a 256 -c SHA256SUMS # macOS
sha256sum -c SHA256SUMS # LinuxFull changelog:
https://github.com/ajsap/mailferry/blob/main/CHANGELOG.md