Skip to content

Security & privacy

Out of the box Inboxrake has no login: anyone who can reach the port can read your mail metadata and reconfigure the IMAP host or AI endpoint (and so read the stored credentials). Pick one of:

  • keep the port on localhost, or behind a reverse proxy with its own authentication (Traefik + tinyauth/Authelia, …), or
  • turn on the built-in login in Settings -> Server -> Login: a password (scrypt-hashed, session cookie), or SSO via any OpenID Connect provider (Pocket ID, Authentik, Keycloak, …). Register the app at the provider with the callback URL https://your-host/api/oidc/callback, then enter issuer, client ID and secret. Both can also be bootstrapped from the environment (AUTH_MODE, AUTH_PASSWORD, OIDC_* - see deploy/.env.example).

Docker published ports bypass ufw, so a host firewall is not enough.

Password login throttling

Password login allows 5 attempts per 15 minutes per client address (IPv6 per /64); further attempts get a “try again in N minutes” answer without any password check, even with the right password. Attempts are counted before the check, so parallel requests do not get around the limit. The counters live in memory only: a restart forgives.

Inboxrake never trusts X-Forwarded-For itself. Behind a reverse proxy every visitor therefore shares the proxy’s address, so 5 wrong passwords from anyone lock everyone out for 15 minutes, unless uvicorn is told which proxy to believe. Set FORWARDED_ALLOW_IPS in the container environment to your proxy’s address, or to its network in CIDR form (for example the Docker network’s subnet), and attempts are counted per visitor. Never set it to *: uvicorn then believes the leftmost X-Forwarded-For entry, which the visitor writes themselves (proxies append to the header they received), so anyone can dodge the throttle by sending a different fake address with every attempt.

The trusted address must carry only the reverse proxy’s traffic. Prefer the proxy container’s own address over a whole Docker subnet: the subnet includes its gateway, and anything that reaches the app through Docker’s own port forwarding (a process on the host, or IPv6 clients when the port is published without IPv6 on the bridge) arrives from the gateway and could then fake X-Forwarded-For. When a proxy fronts Inboxrake, keep INBOXRAKE_BIND=127.0.0.1 (the default) or do not publish the port at all.

Behaviour worth knowing

  • Host check (no login only). Without a login, Inboxrake refuses requests whose Host is not localhost or an IP address (protection against DNS rebinding), and refuses cross-site writes. To open it under a host name add INBOXRAKE_ALLOWED_HOSTS=<name> (comma list, *.example.org for subdomains) to the container environment. A blocked browser sees a page that says so.
  • Password mode slows down repeated wrong passwords per client.
  • Sessions. Changing the password, the login method, the allowed users, the admin or the identity provider (also via .env + restart) signs every browser out, as does Settings -> Server -> Sign out all sessions. Logging out only clears that browser’s cookie.
  • SSO and email_verified. An identity returning an email must also return email_verified: true. Missing, false or malformed verification is refused. Tenant ownership uses the immutable issuer and provider subject, not email. Keep the allowed-users list filled: empty means every account at the provider may sign in and gets its own workspace (a startup warning says so).

Multi-user (SSO mode)

With SSO enabled, every signed-in identity gets its own isolated workspace: their own mail accounts, scans, AI verdicts, rules, statistics and settings - nothing is shared or visible across users. One identity is the admin (set OIDC_ADMIN or enter it in settings; first-login bootstrap is allowed only with an allow-list containing exactly one identity): the admin owns the workspace that existed before SSO was enabled, plus the server-level settings (login config and the optional shared AI key - users without their own key can use the admin’s, capped by a per-user monthly budget). Password mode and “none” stay single-workspace.

Secrets encryption at rest

Set INBOXRAKE_SECRET_KEY (e.g. openssl rand -base64 32) and every stored secret - IMAP passwords, AI API keys, the OIDC client secret, OAuth client secrets and tokens - is encrypted on disk (Fernet/AES). That protects volume backups: without the key, a copied /data contains no usable credentials. Keep the key in the environment only, never in the backups; if it is lost, re-enter the secrets. Unset = plaintext (a startup log line reminds you).

Microsoft tenant

Outlook / Microsoft 365 sign-in uses the consumers tenant by default (personal accounts). For a work or school tenant set INBOXRAKE_MS_TENANT to its tenant ID or domain (or organizations / common); the shared client ID, if you run one, is INBOXRAKE_MS_CLIENT_ID (see install.md).

The safety promises

  • Nothing is deleted without asking. Trashing is an IMAP move to the Trash folder, behind a confirmation (the mail count; a single protected group or pinned mail asks “anyway?” naming it instead). UIDVALIDITY is checked before acting on cached UIDs, and scans are read-only.
  • Moves normally offer Undo, limited to the last 10 actions until a restart. When the server cannot identify the moved copies safely, the entry is kept and recovery must be done manually from the destination. Permanent-delete actions in Trash and Junk cannot be undone. The manual ones confirm with exact counts; an opt-in purge rule only acts after you have reviewed a report run, then runs on its schedule within its age limit and per-run cap. Rules start in report mode too.
  • Protected senders and pinned mails: rules and the Folders-panel purges always skip them. Moving a protected group, or a single pinned mail, to Trash asks for an extra confirmation. Individual-message moves refuse protected senders until they are unprotected in their group. Empty Trash empties everything in Trash, protected or not.
  • Accounts stay strictly separate - nothing is aggregated across accounts, and with SSO nothing is shared between users.

What the optional AI review sends

Only mail metadata, and only to the AI provider you configure (a cloud provider or a local model such as Ollama): sender names and addresses, mail counts, sizes, dates, read state, category tags, subject lines, protected-sender markers and whether you ever replied to a sender. Mail bodies and attachments are never sent. AI sorting of the “Other” row sends, per sender, its name and address, mail count, whether it offers an unsubscribe link and up to three recent subject lines, plus your own sorting instructions. Describing a filter in words sends only your sentence, the filter syntax and the names of your tags. The review is off until you set it up and consent in the UI.

Reporting a vulnerability

See SECURITY.md.

SSO identity upgrade and legacy workspaces

Upgrading to immutable SSO identities invalidates existing SSO sessions; sign in again. The configured administrator binds to the first verified matching provider subject and keeps the existing root workspace. Later identities with a recycled email cannot take it over. Configure the admin before exposing SSO with a broad allow-list.

Existing non-admin email-owned tenant directories are retained, but are not automatically assigned to new identities. Before upgrading, export each user’s settings backup. Restore it in the new workspace and reconnect the mail accounts, or explicitly map the old directory identity to the new one with INBOXRAKE_OIDC_LEGACY_BINDINGS. This environment variable is a JSON object mapping an immutable oidc:<sha256> identity to the old email/subject, for example {"oidc:<identity hash>": "member@team.example"}. The signed-in user’s immutable identity is returned as identity by /api/auth. Only the operator should create these mappings after verifying the provider subject; never derive them automatically from matching emails.

Importing a settings backup never imports secrets. If it changes a mail server, account identity, transport/certificate settings, or the AI provider endpoint, the corresponding stored credentials are cleared. Re-enter them before connecting to the changed endpoint.