A local-first macOS Gmail client built with Tauri 2 (Rust) and React + TypeScript. Your mail is fetched from Gmail and cached on your Mac in a local SQLite database; OAuth tokens live in the macOS Keychain. Nothing is sent to any third‑party server — the app talks only to Google's APIs.
Personal project. Ember is not affiliated with Google.
Prebuilt macOS builds are published on the Releases page (Apple-Silicon, macOS 13+, unsigned). See INSTALL.md for the full walkthrough — getting past Gatekeeper and connecting Google.
Heads-up for downloaders: Ember uses Google's restricted Gmail scope, so a public build can't ship a shared key — on first launch you paste your own Google OAuth Client ID + secret (the app explains how, and it's a one-time setup). This makes public builds practical mainly for technical users. Why this is unavoidable — and the other options (Test users, verification) — is covered in INSTALL.md. Maintainers: see RELEASE.md for how releases are cut.
- Multiple Google accounts — add several accounts and switch the active one from the avatar menu; Gmail, Calendar, and notes all follow the active account. New mail for all connected accounts is polled in the background with native notifications.
- Smart inbox — messages are classified into People / Notifications / Newsletters streams.
- Full mail workflow — read, compose, reply / reply‑all / forward, drafts, attachments (send & receive), labels, and folder views (Sent, Starred, Archive, Trash, Spam, Drafts).
- Batch actions + undo, snooze (archive now, resurface later), and server‑side search.
- Calendar — week view plus create / edit / delete events (with optional Google Meet links).
- Meeting notes — local, per‑event notes; optional local‑LLM summarization via Ollama and transcript import.
- Local‑first & private — mail cache in SQLite, tokens in the Keychain, no external backend.
- Backend: Rust, Tauri 2,
rusqlite(SQLite),keyring(macOS Keychain),oauth2(PKCE + loopback). - Frontend: React 19, TypeScript, Vite.
- macOS 13 (Ventura) or later — the app's minimum (meeting‑transcription capture uses ScreenCaptureKit, which needs macOS 13+); the prebuilt
.dmgis Apple‑Silicon (aarch64) - Node.js 18+ and npm
- Rust toolchain — install via rustup
- Xcode Command Line Tools —
xcode-select --install - A Google Cloud OAuth client (see below)
Ember needs your own Google OAuth credentials.
- In the Google Cloud Console, create a project.
- APIs & Services → Enable APIs → enable Gmail API and Google Calendar API.
- APIs & Services → Credentials → Create credentials → OAuth client ID → application type Desktop app. Copy the Client ID and Client secret.
- OAuth consent screen / Audience → add the Google account(s) you'll sign in with as Test users.
⚠️ Ember requests the full Gmail scope (https://mail.google.com/), which Google classifies as restricted. For an unverified app, restricted scopes only work for accounts on the Test users list — there's no "continue anyway" bypass. Adding a test user is a one‑time step per account.- In Testing publishing status, refresh tokens expire after ~7 days (you'll re‑sign‑in periodically). Removing that entirely requires Google's formal app verification.
- Create your env file:
Then edit
cp src-tauri/.env.example src-tauri/.env
src-tauri/.envand paste your values:EMBER_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com EMBER_GOOGLE_CLIENT_SECRET=your-client-secret.envis gitignored and never committed.
npm install
npm run tauri devThis compiles the Rust backend (first run takes a few minutes), starts Vite, and opens the native window. The credentials are read from src-tauri/.env at runtime.
There's also a browser‑only "maket" (
npm run dev) that renders the UI with mock data for quick frontend work — it can't do real Google sign‑in.
npm run tauri buildProduces a .app and .dmg under src-tauri/target/release/bundle/. The build embeds the credentials from src-tauri/.env into the binary (via build.rs), so the bundle is self‑contained and runs on another Mac without the source tree.
- Apple Silicon → Intel (or both): build a universal binary with
rustup target add x86_64-apple-darwin && npm run tauri build -- --target universal-apple-darwin.
Ember can be shared with other people, each using their own Google Cloud project:
- Build the
.dmgwithout baking your credentials — just don't shipsrc-tauri/.env(an absent/empty.envmeans nothing is baked in). - On first launch, each user is asked to paste their own Client ID + secret (stored in their Mac's Keychain). They follow the same Google OAuth setup as above and add themselves as a Test user of their own project.
- Credentials can be updated or cleared anytime in Settings → Google API.
(Your own personal build that includes src-tauri/.env keeps working with no entry — the
baked credentials are used automatically.)
For public releases this is automated: pushing a v* tag runs
.github/workflows/release.yml, which builds the BYO
.dmg on an Apple-Silicon runner, attaches a SHA-256 checksum, and creates the GitHub
Release. Step-by-step (and the manual fallback) is in RELEASE.md.
- Copy the
.dmg(e.g.Ember_0.1.9_aarch64.dmg) to the other Mac and drag Ember into Applications. - The app is unsigned / not notarized, so Gatekeeper blocks the first launch. Either:
- System Settings → Privacy & Security → "Open Anyway", or
xattr -dr com.apple.quarantine /Applications/Ember.appin Terminal, then open it.
- On first sign‑in, allow Ember to use the login Keychain (it stores your OAuth tokens there).
- The account you sign in with must be a Test user of your OAuth app (see above).
For a clean, no‑warning install you'd need an Apple Developer ID certificate + notarization (a paid Apple Developer account) — optional and only worth it for wider distribution.
- Click the avatar at the bottom of the icon rail → the account popover lists your accounts (active marked, with unread counts).
- Add account → runs Google sign‑in; the new account becomes active.
- Click any account to switch — the inbox, folders, and calendar follow it.
- Manage in Settings → remove accounts individually.
Transcription runs in‑process — Whisper is compiled into Ember (no separate server, no manual install). The first time you Record in a meeting note (or Import a recording), Ember downloads the speech model (base.en, ~142 MB, one time) to its app‑data folder and loads it; after that it's instant and fully offline.
Capture is native and zero‑setup — no virtual audio device, no BlackHole. Ember records the call's audio straight from the system output via ScreenCaptureKit:
- Click Record in a meeting note. The first time, macOS asks for Screen Recording permission → Allow (you may need to relaunch Ember for it to take effect).
- To also include your own voice, tick "Also capture my voice" before recording — macOS will additionally ask for Microphone permission.
- macOS version: system‑audio capture needs macOS 13+; mixing in your own mic needs macOS 15+ (on macOS 13–14 only the call / other participants are captured).
Summaries (distinct from transcription) run locally via Ollama:
# install Ollama from https://ollama.com, then:
ollama pull llama3.2With Ollama running, the Summarize button in a meeting note produces a summary from the note + transcript. No data leaves your machine.
- Shared‑event notes: meeting notes are keyed by
(calendar_id, event_id)without the account. If two connected accounts are both on the same shared calendar event and both take local notes, the second save overwrites the first (local data loss only — never a cross‑account leak). - Live, real‑OAuth multi‑account flows are validated manually; the automated tests cover the Rust backend and the browser maket.