Checker for Gmail™
Primary navigation Security Local mail component Chrome Web Store

Security Architecture and Data Flow

Last updated: August 14, 2026

This page explains how Checker for Gmail™ separates Gmail, IMAP/SMTP, KeyLockr, and the SafeX website backend. The core rules are that mail data never enters the SafeX backend, long-lived secrets persist only within the smallest necessary boundary, and plaintext keys exist only in memory or the browser session for the operation that needs them.

1. Trust-boundary overview

Checker for Gmail™ trust-boundary data flow Three trust zones: your device, SafeX, and external services. Thick lines are mail data and none of them enter the SafeX zone; thin lines are OAuth authentication values; dash-dot lines are mail account credentials; dashed lines are local encrypted storage; dotted lines are optional paths. Your device External services SafeX gmail.safex.cloud Gmail API to extension: bodies / search ←, actions / reply / send → those requests carry the Bearer access token encrypted portable IMAP/SMTP account bundle only Chrome Identity browser built-in, Chrome only Google OAuth accounts.google.com Checker for Gmail™ extension inside your browser Gmail Atom mail.google.com, browser sign-in Local encrypted vault IDB ciphertext; local manifest; session key Gmail API gmail.googleapis.com KeyLockr, optional encrypted IMAP bundle; no message bodies Local mail component same-device program, optional Your IMAP/SMTP provider third-party service Stateless OAuth proxy /oauth/exchange · /oauth/refresh Legend mail data OAuth authentication values mail account credentials local encrypted storage optional path In one minute Mail and attachments never enter SafeX. Sent and received mail flows only between your device and your mail provider. With KeyLockr on, only the encrypted portable IMAP account bundle syncs. Non-Chrome OAuth values are encrypted in transit; plaintext exists only briefly in proxy process memory, never stored, never logged. SafeX also serves this site's static disclosure pages and local-component releases, outside this flow.
Trust boundaries and data flow: thick lines are mail data, and none of them enter the SafeX zone.
  • The extension connects directly to Gmail Atom, the Gmail API, or the IMAP/SMTP provider you select. Message lists, bodies, attachments, searches, and mail actions are never sent to gmail.safex.cloud.
  • Google Chrome uses Chrome Identity. Chrome manages the access token, and the extension does not persist it.
  • Supported non-Chrome Chromium browsers use Google Web OAuth. SafeX provides only the stateless /oauth/exchange and /oauth/refresh proxy endpoints; it does not provide a Gmail API proxy.
  • IMAP/SMTP connects to the provider through the optional same-device local mail component. Neither the extension nor that component sends mail or credentials to the SafeX website.

2. Web OAuth transport protection

  1. The extension generates a PKCE verifier/challenge and random state, and accepts only an exact redirect origin, path, and state match.
  2. Every exchange or refresh creates a fresh ephemeral X25519 key and nonce. The extension uses the SafeX X25519 public key pinned in the package and an authenticated box to encrypt the authorization code, PKCE verifier, or refresh token.
  3. The proxy authenticates the ciphertext, decrypts it only in process memory, injects the Google client secret held only in the server environment, and forwards the request to Google. The Google response is encrypted back to that request's ephemeral client key.
  4. The proxy rejects plaintext, malformed, or unauthenticated requests and responses. OAuth authentication values are not written to a database, file, server session, application log, or /oauth access log.

This application-layer encryption prevents TLS/CDN intermediaries from reading or modifying the OAuth payload. Intermediaries may still observe source, destination, timing, and traffic size. Because the proxy must obtain plaintext in memory before forwarding to Google, this is not end-to-end encryption from the extension to Google.

Wire v1 does not keep a server-side nonce replay set. A captured encrypted refresh request can be replayed, but without the original ephemeral private key the attacker can observe only encrypted response status or timing and cannot decrypt the response. HTTPS, production in-memory rate limits, no OAuth access log, and a fresh nonce for each extension request bound this risk. A future wire version should add explicit direction separation and replay hardening.

3. Local browser OAuth vault

The diagram below shows when this vault stands in your way: which paths require it, that the first time you choose between a local password and KeyLockr, and that the two tasks are independent of each other.

When Checker for Gmail™ asks you to choose an encryption method Two independent paths. Connecting Gmail on Google Chrome uses Chrome Identity and needs no encryption choice; on other Chromium browsers the encrypted vault must be ready first, and only then can web OAuth authorization happen. Adding an IMAP/SMTP account always needs the encrypted vault, in every browser including Chrome. Making the vault ready means choosing a local password or KeyLockr the first time, and unlocking the existing method afterwards. already unlocked already unlocked encrypted refresh grant Task A: connect Gmail Google Chrome Chrome Identity; no encryption choice The extension does not persist the token. Other Chromium browsers Web OAuth; the refresh grant must persist A1. Make the encrypted vault ready Not initialized: choose. Locked: unlock. Already unlocked: passes through. Local password set or unlock KeyLockr pair or approve A2. Authorize with Google only after the vault is ready Done: Gmail connected Task B: add an IMAP/SMTP account B0. Local mail component ready permission granted and host registered Any browser, including Chrome. B1. Make the encrypted vault ready Not initialized: choose. Locked: unlock. Already unlocked: passes through. Local password set or unlock KeyLockr pair or approve B2. Enter IMAP/SMTP details host, port, user, password Done: account added Profile-local OAuth container chrome.storage.local, encrypted Access tokens: session only, not here. OAuth values never reach KeyLockr. Legend user flow OAuth data
This diagram shows only the paths that complete successfully. Key-change recovery and an unreadable OAuth container are covered in Section 6; installing and registering the local mail component is covered on the Local mail component page. Other failures are handled by prompts inside the extension.
  • Each browser profile generates its own random vaultId. The vaultId is a public local salt, not a password or key.
  • k_oauth is derived from a random data-encryption key (DEK) with HKDF-SHA-256. Derivation binds both an independent OAuth crypto domain and that profile's vaultId, so it is not reused as a mail, lookup, manifest, or AppData subkey.
  • Each account's refresh grant is encrypted with AES-256-GCM and AAD containing the schema, vaultId, keyId, and account key before it is written to chrome.storage.local. Moving ciphertext between accounts or vaults causes authentication to fail.
  • Short-lived access tokens never enter persistent storage and exist only in chrome.storage.session. Each record is bound to the current grant by a SHA-256 digest of the refresh token, preventing a stale session token from being used with a replacement grant. Closing the browser removes the access token and unlocked DEK.
  • A legacy plaintext grant is migrated only after the vault is unlocked. A successful encrypted write atomically replaces the old plaintext container. Key mismatch, ciphertext corruption, or a locked vault fails closed and never deletes data or falls back to plaintext.

4. Password mode

The user password derives a key-encryption key (KEK) with Argon2id13 using three operations and 64 MiB of memory. The KEK is used only to unwrap a random 256-bit DEK; neither the password nor KEK is persisted. The manifest stores only KDF salt/parameters, the AES-GCM-wrapped DEK, a non-secret keyId, and an integrity MAC. After unlock, the DEK is stored only in chrome.storage.session, so the password is entered once per browser session. SafeX cannot recover a forgotten password.

5. KeyLockr mode

  • The KeyLockr data_filekey is not a user password and is not used directly as an OAuth encryption key. It is a high-entropy KEK released briefly after phone authorization, used only to unwrap the vault DEK, and overwritten when the callback ends.
  • KeyLockr AppData stores and synchronizes only the encrypted portable IMAP/SMTP account bundle: account settings, credentials, local-mail policy, and server-retention state. KeyLockr is an optional third-party remote persistence service. It sees authenticated ciphertext and required non-secret envelope metadata (schema version, key ID, app tag), but that remains a third-party storage boundary.
  • The canonical MsgPack envelope sent to KeyLockr contains exactly header, wrappedDEK, and stateCt; decrypted stateCt contains only schemaVersion, accounts, and updatedAt. app_set_data uses an opaque revision for compare-and-swap. The local keylockr replica is only a verified copy of the remote envelope and revision, not storage for profile-private content.
  • Templates (profile-level), per-account signatures, and scheduled-send jobs (recipient, subject, body, time, and state) use an independent HKDF domain and remain encrypted only in browser-managed IndexedDB database safex-password-vault, object store replica, record key keylockr-profile:<keyId>. The manifest is only in chrome.storage.local and the unlocked DEK only in chrome.storage.session; no user-visible plaintext JSON file exists.
  • Templates, signatures, scheduled messages and bodies, Gmail data, OAuth tokens/grants/ciphertext, the user vault password, and k_oauth are never written or sent to KeyLockr.
  • Two profiles may share the same encrypted IMAP/SMTP account bundle and DEK through KeyLockr. Each profile keeps independent templates, signatures, scheduled sends, local vaultId, and OAuth ciphertext. Profiles may therefore reuse the same IMAP settings while connecting different Gmail accounts without obtaining each other's private content.

Public AppData packet fields

This table describes only the format and data categories. It contains no real key, ciphertext, account value, or server secret.

Layer / field Wire shape Data boundary
header schemaVersion, keyId, app_tag Fixed non-secret envelope metadata; schemaVersion is currently 2.
wrappedDEK {iv, ct} Authenticated DEK ciphertext wrapped by the data_filekey released briefly after phone authorization.
stateCt {iv, ct} After decryption, exactly schemaVersion, accounts, and updatedAt; accounts contains only portable IMAP/SMTP settings, credentials, and related policies.
revision Opaque CAS token (outside the canonical envelope) app_set_data commits only while the revision read by the client is still current; a conflict fails visibly.

6. Revocation, deletion, and key changes

  • Disconnect first removes the selected account's local encrypted grant and session access token, then makes a best-effort revocation request directly to Google. The extension does not proactively delete other accounts' local grants, but Google revocation may also invalidate other tokens for the same project; an affected account will require authorization again.
  • When Google reports invalid_grant, the extension clears data only if the current grant still matches the refresh snapshot, preventing an older request from deleting a newer authorization.
  • If a retired review build left an OAuth container tied to a different vault DEK/keyId, an upgrade does not attempt unauthenticated rewrapping or legacy-key migration. Startup and inspection do not delete ciphertext. The extension lists the container's public account indexes and clears the complete local container and session access tokens only after one explicit user confirmation, then requires each affected account to be authorized again. Because the refresh token cannot be decrypted, this recovery path cannot revoke access at Google; the user can revoke it manually from Google permissions. An ordinary password-to-KeyLockr or KeyLockr-to-password mode switch that preserves the same DEK does not affect OAuth grants.
  • Uninstalling the extension causes the browser to remove that profile's local and session storage. Removing a KeyLockr device pairing does not delete the remote AppData still used by other devices.

7. Local-component update trust chain

  • Update checks occur only after the user has granted the optional nativeMessaging permission. The extension service worker then invokes the separate com.safex.mail_updater host; without permission it does not probe, and there is no resident background daemon.
  • The extension receives only installed and target versions plus fixed status codes. It displays an Accounts-page notice when an update exists and requests installation only after the user selects “Update now.” The extension does not use the downloads permission and does not directly download or execute programs.
  • The updater is pinned to gmail.safex.cloud/native-mail/update/v1 and rejects cross-origin redirects. Its strict manifest must pass the dedicated SafeX Mail Ed25519 key, monotonic sequence, architecture, protocol, exact-size, and SHA-256 checks. Every Mach-O must also pass Apple Developer ID, Team ID YG785Y343S, identifier, hardened-runtime, secure-timestamp, designated-requirement, and Gatekeeper verification.
  • Helper, updater, and bridge install through a same-filesystem staging, backup, and journal transaction, with the bridge last as the compatibility commit point. Failure or interruption rolls back; an unsafe rollback explicitly requires manual reinstallation. Publishing uploads payloads first, switches the signed manifest second, and removes old payloads last. The website update endpoints return 404 for any missing file, signature mismatch, or hash mismatch.

8. Backend and testing controls

The production website service has no mail database, token database, migrations, or server-side user sessions. Each OAuth handler is a standard-library http.Handler built with immutable configuration, preventing tests or multiple services that share the package from overwriting one another's client or key settings. Sensitive OAuth endpoints write fixed JSON responses directly and do not use generic success helpers that add an envelope or log responses.

The authorized security-testing environment is a minimal separate service. It registers only a root description, healthz, strict /key_enc, and the two OAuth endpoints; generic signing, build-version, and .well-known routes are not registered at all. It stores no token or mail data and may disable in-memory rate limiting under written authorization for DAST. A test extension package is visibly marked with its origin and public-key fingerprint and uses a separate test artifact name (the test-chrome-mv3 directory and a -test-chrome.zip archive) so it cannot be confused with the production store package.

9. Security limitations

Local encryption protects persistent storage while the browser is closed and limits data exposure across profiles and remote services. It cannot protect a running browser session that is fully controlled by malware, a browser vulnerability, a privileged malicious extension, or an attacker already signed in to the operating-system account. Users should keep the browser and operating system current, install only trusted extensions, and close the browser or lock the vault when it is not in use.

10. More information

For data categories, retention, and user deletion controls, see the Privacy Policy. For manual installation, updates, and verification of the local mail component, see Local mail component.

© 2026 SafeX Inc. All rights reserved.

Gmail™ is a trademark of Google LLC. Checker for Gmail™ is an independent product, not affiliated with or endorsed by Google.

Footer navigation Privacy Terms Feedback