Read-only by construction
Handing an assistant your mailbox is a real decision. The answer here is not a setting you have to trust — it is that the code to modify your mail was never written, and a test fails the moment someone writes it.
The four guarantees
Each one is a property of the code, with the file that carries it.
Folders are opened with EXAMINE
IMAP has two ways to open a mailbox: SELECT, which gives read-write access, and EXAMINE, which is read-only at the protocol level. Every folder this server opens uses EXAMINE. The server is not choosing to behave — the session it negotiated cannot carry a write.
backends/imap.pyBodies are fetched with BODY.PEEK
A normal IMAP fetch sets the \Seen flag as a side effect — which is how a mail client marks things read. BODY.PEEK is the variant that does not. Your assistant can read a thread and your unread count will not move.
mime.pyThere are no write code paths
No send, no move, no delete, no flag, no append, no expunge — not disabled behind a flag, absent. The MailBackend protocol every backend implements states the rule in its own docstring: no implementation may set flags, move, delete, append, or expunge.
backends/base.pyA test keeps it that way
The fake IMAP server the suite runs against raises on every mutating call, and a separate regression test scans the backend for them. Adding a write does not quietly ship — it fails CI.
tests/fake_imap.py
How your data is handled
Your mail never leaves your machine
The server runs locally and talks to your assistant over stdio. There is no cloud component, no relay, and no account to sign up for. Mail goes from your provider to your machine and no further.
Credentials live in the OS keyring
Tokens and app passwords go to the system credential store — Secret Service on Linux, Credential Manager on Windows. Where no keyring is reachable they fall back to a file created mode 0600 from the start. config.toml never holds a secret.
No telemetry, of any kind
Nothing is counted, sampled or phoned home. The only network connections it makes are to your mail provider and, for Outlook, to Microsoft’s sign-in endpoint.
Attachments cannot escape their directory
get_attachment is the only thing that writes anything. Filenames are sanitized, cannot traverse upwards out of the configured download directory, and never overwrite an existing file.
Only the text parts are downloaded
Bodies are located via BODYSTRUCTURE and truncated at 20,000 characters by default, so a mail with a 20 MB attachment costs kilobytes and a newsletter cannot flood the context.
You can narrow it further
Any account-scoped tool can be switched off per account with disabled_tools — keeping attachments off a shared mailbox, say, or leaving a work account listed but unreadable.
What this does not protect against
A security page that only lists strengths is marketing. These are the real edges.
The installers are unsigned, so Windows Defender and Avast may flag them. That is a known false positive for PyInstaller binaries, and code signing is a real follow-up, not done yet. Every published SHA-256 is on the changelog so you can verify what you downloaded.
This does not constrain what your assistant does with what it reads. It guarantees your mailbox is not modified; it cannot guarantee a model will not quote a message back to you in a context you did not expect.
A prompt injection in an email is a real risk for any tool that feeds mail to a model. Reading is safe for your mailbox; acting on what you read is your assistant’s business, not this server’s.
Your mail provider still sees ordinary IMAP access from your machine, and for Outlook, Microsoft sees an OAuth grant against your own Azure app registration.
The site you are reading is static hosting on GitHub Pages, which does not let a project set response headers — so no claims are made about the security headers of this page.
Reporting a vulnerability
Please don't open a public issue first. Use the contact form and pick Security disclosure — it reaches a person directly.
Report privately