Privacy
We literally can’t read your email.
mailctl is built around a hard constraint: we should not be able to read your messages. This page is the technical proof of that constraint.
TL;DR
✓We request the narrowest OAuth scopes that exist.
✓Message bodies and attachments are physically inaccessible.
✓Refresh tokens are encrypted at rest (AES-256-GCM).
✓Disconnect an account → its data is deleted immediately.
✓We store
- · provider message id + thread id
- · from address, from name
- · subject, date, size in bytes
- · List-Id and List-Unsubscribe headers
- · labels / folders + derived flags (unread, promo, social)
✕We never store
- · message bodies
- · attachments
- · snippets or previews
- · other recipients (To / CC / BCC)
- · anything outside the scopes below
What we ask for
Gmail
gmail.metadata— sender, subject, date, size, labels, and the headers we explicitly request (List-Id,List-Unsubscribe). This scope is physically incapable of returning message bodies or attachments.gmail.modify— needed to archive / delete / mark-read in bulk. It does not grant body access.openid email— to know which mailbox you connected.
We do not request gmail.readonly, gmail.send, gmail.compose, or any restricted scope.
Outlook (Microsoft 365 / Outlook.com)
Mail.ReadBasic— message metadata and headers; excludes bodies and attachments by design.Mail.ReadWrite— for moving messages between folders and toggling read state.User.Read,offline_access,openid,email.
Refresh tokens
OAuth refresh tokens are encrypted at rest with AES-256-GCM using a key held only by the application server, separate from the database. Losing the database alone does not yield usable mailbox credentials.
Subprocessors
Supabase— Postgres database and auth
Vercel— hosting for the web app
Inngest— orchestrates background jobs
Sentry— error reporting (no message content)
Deletion
Disconnect an account from /dashboard and we delete its messages, actions, and rules. Email us to wipe the entire user record.
This page is the source of truth. If a feature would require widening scope, it must change this page first.