Skip to content

Privacy and security ​

Swanmail is built around the idea that your email is yours. This page explains what stays on your device, what leaves it, and how you can lock things down further.

No telemetry ​

Swanmail does not collect analytics, crash reports, usage statistics, or any other telemetry. There are no tracking pixels, no third-party analytics SDKs, and no mandatory cloud services.

Local-first data ​

  • All email content, metadata, and attachments are stored in a local SQLite database.
  • The database lives in Swanmail's data directory:
    • macOS: ~/Library/Application Support/swanmail/
    • Windows: %APPDATA%\swanmail\
    • Linux: ~/.config/swanmail/ (or $XDG_CONFIG_HOME/swanmail/)
  • Search indexes (SQLite FTS5) are also local.
  • Drafts are saved locally and can optionally be synced to your provider's IMAP Drafts mailbox.

Encrypted credentials ​

Passwords, OAuth tokens, API keys and private keys are encrypted before they are stored. On macOS the key is kept in the data directory's safe-storage.key file, which only your user account can read. It is not in the keychain: Swanmail is not signed with an Apple Developer ID, so macOS would ask for your login password after every update. Any program that runs as your user can read the file, so FileVault and your account's password are what protect it. On Windows it is a random key kept in the data directory's Local State file, protected with the Windows Data Protection API (DPAPI) for your Windows account. On Linux it is kept in the Secret Service (GNOME Keyring or KWallet); without one, it is a random key in the data directory's safe-storage.key file, which only your user account can read. Older versions used a fixed key there, which did not protect the values; they are still read, and a password you enter again is stored under the new key. The command-line tool never reads the keychain: it uses the app's key file (or, on Windows, Local State) when it can, and otherwise a key made from the machine's name, your user name and a network address, which do not protect the password against someone with a copy of the database.

OAuth ​

A release build carries its own Google and Microsoft OAuth client IDs, filled in when it is built; the repository holds none. These credentials are only used to initiate the OAuth sign-in flow, which runs in your web browser; the resulting refresh token is stored encrypted on your device, and access tokens are kept in memory only.

If you prefer, you can replace the default OAuth credentials with your own by editing the app's config.json. See the Accounts page for details.

AI and LLM features ​

AI features are opt-in and only appear after you configure an agent:

  • Local CLI agents keep all email content on your machine.
  • LLM API agents send prompts to the endpoint you configure (OpenAI, Groq, Ollama, etc.). No LLM call is made unless you explicitly add and enable an LLM API agent.

When using a cloud LLM API, your email content is sent to that provider under their terms. For maximum privacy, use a local Ollama server.

GPG encryption ​

Swanmail supports optional OpenPGP signing and encryption, of HTML and plain-text messages alike. Encrypted messages can only be read with your private key, which is encrypted at rest like every other secret.

S/MIME ​

S/MIME identities are imported from a .p12 / .pfx file; the private key is encrypted like every other secret. An encrypted message is decrypted for the view you have open and never written back in the clear. See S/MIME.

Remote images ​

Remote images are blocked by default to reduce tracking-pixel leakage. You can show them once for a message, turn on the global reading preference, or save a per-account exception for the normalized sender address or sender domain. These exceptions are stored locally and are not shared between accounts.

Blocking is enforced by the Content-Security-Policy of the sandboxed frame a message is shown in, so it also stops srcset candidates, CSS background images and web fonts — not only <img> tags. A "show once" choice belongs to the message it was made on and never carries over to the next one you open.

When images are allowed they are fetched by the app's backend and handed to the message as data. That fetcher only speaks http: and https:, refuses loopback, link-local and private-network addresses (including names that resolve to them), checks every redirect hop, and abandons a batch after 2.5 seconds.

Links always open in your system browser. A link whose visible text looks like one address while it leads to another host, or whose destination is a punycode or mixed-script domain, is marked, and the message shows a count of such links. The check runs locally on the message's HTML.

Read receipts and unsubscribe ​

Swanmail never answers a read-receipt request by itself; a banner asks, and your choice is remembered per message. Unsubscribe acts only after you confirm: a one-click request is an HTTPS POST from the app's backend to the address the sender published (private-network addresses are refused); other senders get an email you can read before sending, or their page in your browser.

Tags, rules and other local state ​

Rules, snoozes, muted and pinned conversations, templates and scheduled messages live only in the local database and settings. None of them is synced to your mail provider. Tags stay local too, except on Microsoft 365 (Graph) and Exchange Server (EWS) accounts, where they are the messages' Outlook categories and sync with the server.

Network connections ​

Swanmail connects directly to your email provider's servers (IMAP, SMTP, POP3, JMAP, Microsoft Graph or Exchange Web Services) and any OAuth identity provider you choose. There is no Swanmail backend or proxy server in between. Beyond that, it reaches out only for these:

  • Gmail API, for a Gmail account signed in with Google: new-mail signals and server search.
  • Sender avatars. For a sender's domain, Swanmail looks up its BIMI record in DNS, and asks Google's favicon service (t1.gstatic.com) or the domain's own site for its icon. Only the domain is sent. Turn off Settings → General → Reading → Show sender pictures to show initials instead and make none of these requests.
  • Server settings. When you add an account, the domain's MX records are looked up to fill in the servers; an Exchange account may use Autodiscover.
  • Remote images, once you allow them, and one-click unsubscribe, once you confirm it (both described above).
  • OpenPGP key lookups (WKD and keys.openpgp.org), only while Encrypt is on or when you ask.
  • LLM APIs you configure for AI agents.
  • Updates. Once a day Swanmail fetches the latest release's signed manifest from swanmail.app/download/, and on macOS downloads a newer version from there; Settings → About shows the state, checks on request and has a switch, Automatically check for updates, that stops the daily check. The request carries only the app's version. Development builds never check.

Tips for the paranoid ​

  • Use Generic IMAP/SMTP with an app-specific password instead of OAuth.
  • Run a local Ollama server for all AI features.
  • Enable GPG encryption for sensitive outgoing messages.
  • Review and restrict the OAuth scopes you grant to Swanmail in your provider's account settings.