Your meetings,
summarized on your Mac.
calexo-meet is a private, local-first AI meeting assistant — a Tauri fork of Meetily. It captures system + microphone audio, transcribes with Whisper or Parakeet, and summarizes with a local or cloud model, all in one desktop binary. No server, no cloud database. Start with the tutorial, or read how it's built.
Where to go
Interactive tutorial
A step-by-step walkthrough — install, grant permissions, pick devices, record, summarize, run a recipe, and connect Claude over MCP. Your progress is saved as you go.
Begin the tutorial →Architecture
What every part does: the audio pipeline, transcription engines, local SQLite storage, summaries & Recipes, the read-only MCP server, and the draft-only Gmail integration.
Read the reference →Status & roadmap
What shipped in this fork — five Granola-inspired features — plus a Now / Next / Later roadmap and the short list of credentials that still need you.
See the roadmap →The repository
Rust + Next.js source, build scripts, and RELEASE.md for cutting a signed .dmg. Private fork of Zackriya-Solutions/meetily.
The one-line mental model
Audio in → transcript and summary out. Nothing crosses the network unless you explicitly point it at a cloud model or ask it to draft an email. Everything else lives on your disk.
One desktop binary.
Your meetings never leave.
Three layers compiled into one downloadable app. The UI never talks to a network backend of ours — it talks to the Rust core over Tauri's command/event bridge. This page explains what every part does.
The shape of the app
One binary, three layers. The Next.js UI calls Rust commands and listens for Rust events; the Rust core owns audio, storage, transcription, and summary orchestration.
┌──────────────────────────────────────────────────────────────────┐ │ Next.js UI (React / TypeScript, port 3118 in dev) │ │ pages · components · ConfigContext · SidebarProvider │ └───────────────┬──────────────────────────────────▲───────────────┘ invoke('cmd') emit('event') ┌───────────────▼──────────────────────────────────┴───────────────┐ │ Rust core (Tauri 2 · src-tauri/src) │ │ audio · whisper_engine · parakeet_engine · summary · database │ │ mcp · email · ollama/anthropic/groq/openai/openrouter clients │ └───────────────┬──────────────────────────────────────────────────┘ │ on-device ▾ off-device (opt-in) ▾ SQLite · Whisper · Parakeet · Ollama Claude · Groq · Gmail draft
lib.rs The wiring
coreEverything registers here. src-tauri/src/lib.rs is the Tauri entry point — it builds the app, holds shared state, and registers every #[tauri::command] the frontend can call. state.rs holds shared handles, config.rs reads/writes settings, tray.rs the menu-bar icon, onboarding.rs the first-run flow.
Audio pipeline
The app sits on top of your meeting — it never joins as a bot. It captures system audio plus your mic, then splits into two parallel paths: one for a clean recording, one for transcription.
Mic (cpal) + System audio (Core Audio process tap) └──────────────┬──────────────┘ AudioPipelineManager (pipeline.rs) ┌──────────────────────┴──────────────────────┐ Recording path Transcription path RMS-ducked pro mix VAD — speech only │ │ recording_saver.rs whisper / parakeet → .wav on disk → transcript-update events
Capture
on-devicemacOS captures system audio with a Core Audio process tap — no virtual device like BlackHole needed. It does require the system Audio Capture permission (macOS 14.4+). Windows uses WASAPI loopback; Linux ALSA/PulseAudio.
Mix & VAD
on-devicepipeline.rs aligns the two streams in a ring buffer (50 ms windows), applies RMS-based ducking so system audio doesn't drown your voice, and runs Voice Activity Detection so only speech reaches the transcriber.
MicOnly mode & recording commands
opt-inAn opt-in Microphone-only mode drops system audio at the source for sensitive calls. The frontend calls start_recording_with_devices_and_meeting with mic_device_name, system_device_name, and meeting_name (all optional — system audio is optional and falls back to mic-only if unavailable).
Transcription engines
Two interchangeable local speech-to-text engines. Both run on your machine and GPU-accelerate automatically, falling back to CPU.
Whisper.cpp
on-deviceThe default. whisper_engine.rs loads a GGUF model once and caches it, auto-detecting Metal / CUDA / Vulkan. The default model is large-v3-turbo; smaller and quantized variants (base, small, medium) are available too.
Parakeet
on-deviceAn alternative engine with its own model manager and download UI. Same contract — speech in, timestamped transcript out — selectable in Transcript Settings.
Model storage
On macOS, models live in a models/ folder inside the app's data directory (~/Library/Application Support/<app bundle id>/models/). Models are managed in-app; switching one needs a reload.
Storage & state
Everything persists to a single local SQLite database via sqlx migrations. No cloud, no external DB.
database/ The local store
on-deviceA repository pattern over SQLite: manager.rs owns the pool, repositories/ wrap each entity, models.rs defines the rows, setup.rs runs migrations on launch. Meetings support soft-delete — deleting moves to Trash (reversible); "delete forever" purges with cascade.
Frontend state mirror
UISidebarProvider and ConfigContext hold the React-side mirror of meetings, recording status, and settings. The loop: command mutates Rust state → Rust emits an event → a listener updates React → context propagates to components.
Summaries & Recipes
The transcript runs through a pluggable LLM to produce a structured summary. You pick the provider; the default is local Ollama so nothing has to leave the machine.
summary/ The summary path
local defaultprocessor.rs + service.rs orchestrate it; llm_client.rs abstracts the provider; templates/ shape the prompt. Output is markdown, streamed back to the UI's AISummary panel.
Recipes (recipes.rs) are three fixed-prompt actions — Follow-Up Email, Action Items, Coach Me — hard-forced onto local Ollama (the recipe provider is a constant, regardless of your summary settings) so transcripts never leave the device. Summary bullets that emit [mm:ss] become clickable seek links to the nearest transcript moment.
Providers — keys are entered per-user in Model Settings and stored locally; they are never baked into a build.
claudegroqopenrouteropenaiWhere keys live
Cloud keys are optional and typed into Model Settings, saved on-device — not environment variables in a deployment. Using a cloud provider is the one summary path where transcript text leaves your machine; Ollama keeps everything local.
MCP server
A loopback-only server that lets an MCP client — like Claude — read your meetings, transcripts, and summaries and work alongside the app.
mcp/ Loopback, bearer-authed
loopbackserver.rs serves HTTP/JSON-RPC on 127.0.0.1; tools.rs exposes read methods over the same SQLite DB; auth.rs enforces a bearer token with constant-time comparison, regenerated every launch. A fallback_server.rs covers the path where the primary stack is unavailable.
get_mcp_connection_info returns a ready-to-paste mcp.json snippet with the URL and bearer header.
Gmail email
The one deliberate, consent-gated step off-device — built defensively. It creates a Gmail draft from a meeting's follow-up. It never auto-sends; no send endpoint exists.
email/ Draft-only by design
off-device · gatedNarrowest scope (gmail.compose), an explicit consent modal, no PII stored, and the OAuth refresh token kept in the OS keychain (service calexo-meet-gmail). oauth.rs runs a desktop loopback OAuth flow, gmail.rs calls create_draft, compose.rs builds the message.
End-to-end use needs one credential you create: a Google Cloud OAuth client (Desktop app) with the Gmail API enabled, set as GMAIL_OAUTH_CLIENT_ID / GMAIL_OAUTH_CLIENT_SECRET (or a gmail-oauth.json store file).
The UI
A Next.js 14 app rendered inside the Tauri window — a thin, reactive shell over the Rust core.
Routes
app// — the recording interface · /meeting-details — transcript + summary + recipes · /settings — providers, models, transcript engine · /notes — note views.
Components
components/Sidebar (meeting list, Trash, recording state) · MeetingDetails + AISummary · Recipes · model managers · onboarding · Settings.
Run, build & ship
Because it's a desktop app, "deploying" means producing a signed .dmg and putting it on a GitHub Release — there's nothing to host.
Run it locally
From frontend/: ./clean_run.sh (info) or ./clean_run.sh debug. Under the hood that's pnpm run tauri:dev, with GPU variants like tauri:dev:metal. Next.js serves on port 3118 inside the Tauri shell.
Produce the artifacts
./release-macos.sh builds a signed + notarized .dmg plus updater artifacts. UNSIGNED=1 ./release-macos.sh makes a working unsigned .dmg for trusted testers today.
Cut a GitHub Release
Attach the .dmg, the .app.tar.gz + .sig updater payload, and a latest.json. The updater endpoint in tauri.conf.json points at releases/latest/download/latest.json, so installed apps auto-update.
Two one-time setup items before a signed release
① Apple Developer Program ($99/yr) + a "Developer ID Application" cert so Gatekeeper doesn't block the app. ② A Tauri updater signing keypair — generate it and replace the pubkey placeholder in tauri.conf.json. Full steps live in RELEASE.md.
From zero to your first
summarized meeting.
Eight short steps to a working setup — install, permissions, devices, recording, transcription, summaries, Recipes, and connecting Claude over MCP. Click through at your own pace; your progress is saved in this browser.
Five features,
shipped to main.
A private, local-first fork of Meetily — extended with the best of Granola, built in gated phases and verified on a real full-app build. Here's everything new and the short list that still needs you.
What shipped
Five Granola-inspired features. Four are fully local-first; the fifth is the one deliberate, consent-gated step off-device.
F1 Read-only MCP server
donetests 7/7A loopback-only HTTP/JSON-RPC surface so Claude can read your meetings, transcripts, and summaries. Bearer auth with a token regenerated every launch. Read-only by construction.
F2 No-join · MicOnly capture
donetests 4/4Confirmed the app never joins a meeting as a bot — it records system audio from on top. Added an opt-in Microphone-only mode that drops system audio at the source.
F3 Soft-delete Trash
donetests 3/3Deleting a meeting moves it to Trash instead of destroying it. Restore brings it back; "Delete forever" purges with cascade.
F4 Recipes & timestamp citations
donetests 5/5Three built-in Recipes — Follow-Up Email, Action Items, Coach Me — hard-forced onto local Ollama. Bullets that emit [mm:ss] become clickable seek links.
F5 Gmail email · draft-only
code doneneeds 1 credentialtests 17/17The only off-device feature. After a meeting, optionally create a draft in Gmail (never auto-sends). Narrowest scope, explicit consent, token in the OS keychain. Needs one OAuth credential you create.
Roadmap
Where this fork is headed — grounded in the feature plan and the code, not aspiration. Three horizons, no fixed dates. Each item links back to where it's tracked.
get_mcp_connection_info command already returns a paste-ready mcp.json. What's left is a Settings screen to surface it, so connecting Claude is one click instead of a dev command.GRANOLA-FEATURE-PLAN.md · backend in mcp/commands.rs[mm:ss] citations log to the console and memory today. Next is an on-disk diagnostics file plus a Settings view, so transcription-timestamp issues survive restarts.AISummary/brokenTimestampLog.ts (TODO v2)email_dispatched_at column so a "draft created" badge survives restarts. v1 shipped a lighter booleans-only store; this finishes the durable version.GRANOLA-FEATURE-PLAN.md (Feature 5)[mm:ss] out of markdown to structured {text, timestamp} bullets from recipes — a cleaner, more Granola-faithful citation model.Scope note
This is this fork's roadmap. Upstream Meetily PRO features (speaker identification, chat-with-meetings, calendar integration) are a separate commercial track and aren't part of calexo-meet's plan. Larger Granola features (Briefs, team Spaces, semantic search) are noted as intentionally out of scope in the feature plan.
What needs you
The code is done and merged. These are the things only you can do — credentials and account settings.
Gmail OAuth credential
actionThe email feature is wired but to reach Gmail it needs an OAuth client only you can create: Google Cloud → new project → enable the Gmail API → create a Desktop app OAuth client → set GMAIL_OAUTH_CLIENT_ID and GMAIL_OAUTH_CLIENT_SECRET.
Code-signing for distribution
actionTo ship Gatekeeper-clean installs: enroll the Apple Developer Program ($99/yr), generate a Tauri updater signing key, set the signing env vars. Test unsigned today with UNSIGNED=1 ./release-macos.sh.
Deferred · not gaps
Conscious cuts. None block the shipped features from working.
MCP settings panel
v2The get_mcp_connection_info command exists (returns a ready-to-paste snippet), but there's no Settings UI surfacing it yet.
Persistent diagnostics log
v2Broken [mm:ss] citations log to console + memory. A persistent on-disk diagnostics file is the planned next step.