* feat(meetings): Auto-end forgotten meeting recordings Track reliable external microphone activity and show a protected 60-second countdown after meeting activity ends. Preserve recording during reconnects, unreliable detection, and Keep recording actions. * fix(meetings): Harden auto-end against stale binaries and races * fix(meetings): Keep auto-end alive through coverage loss and CI races Code review found that every major failure path killed the feature silently. All fixes preserve the fail-safe property: degradation can only delay or disable auto-end, never stop a live recording. - Windows: a single unattributable session or flaky endpoint made the helper exit, with no respawn and a polling fallback that also lost meeting prompts. It now announces CAPABILITY AGGREGATE once and keeps emitting best-effort events; unattributable sessions refcount under pid 0, which the JS side can never exclude as its own, so they count as external capture and park the countdown. - macOS: a process vanishing between enumeration and its property query permanently downgraded PID mode to aggregate, and the 5s heartbeat re-rolled that race for the whole session. Skip the vanished object and let the next reconcile retry it instead. - Linux: one pactl source-output without application.process.id (echo-cancel, loopbacks) dropped PID reliability entirely. Skip module streams; client streams always carry the pid. - CI: the notarize build raced the listener-publishing workflow and could cache a stale binary under a fresh source hash. The release tag is now pinned to MIC_LISTENER_VERSION in the C source on both ends, and CI downloads fail hard instead of silently falling back. - Renderer: a rejected auto-end stop was swallowed; it is now reported through a required onError hook wired to the meeting logger. * style(notes): Revert drive-by Prettier reflows Restore ShareNoteDialog, OverviewNoteList, and useNoteDragAndDrop to their merge-base content. The reflows were format-only, and the ShareNoteDialog hunk conflicts with the policy-enforcement merge on main for no benefit. * fix(meetings): Exclude own audio helpers and persist auto-end stops Auto-end never fired when system audio was captured: the capture helpers (macOS audio tap, Linux portal, Windows loopback) are plain child processes absent from app.getAppMetrics(), so the OS attributed their capture to non-excluded pids and externalMicActive never went false for the whole recording — verified empirically on macOS via the mic listener reporting the tap helper's pid. Feed the helpers' live pids into the detector's exclusion provider. Transcript persistence was keyed to mounted views, and an auto-end stop is the first stop that can fire while the notes view is closed: the final tail since the last 30s flush was lost and diarization results were dropped. Persist the final transcript in the store's stop path after the main result arrives, move the periodic saver to the always-mounted MeetingRecordingMount, and persist diarization results session-scoped to the note that was recorded. NoteEditor's handler is now display-only and gated to the recording note, so enrichment can no longer land on whichever note happens to be open. Toast when an auto-end request ends the live session — including when a Keep click loses the race with the countdown — so the user learns the recording ended instead of assuming it was kept. New notes.meeting.autoEnded key in all 10 locales. * feat(meetings): Confirm auto-end with audio silence and add a fallback Mic ownership alone had two blind spots: an app that releases the mic on mute could stop a live call while remote voices still played, and the feature was inert wherever ownership is unreliable (older macOS, coverage loss) or never armed (in-person meetings). Combine the two signals in a tick-driven controller. In ownership mode the countdown runs only while the system-audio channel is also quiet, so remote speech defers it; the mic channel is deliberately ignored there so room noise, typing, or talking to a colleague after the call can't mask the end. When ownership is unreliable or no app ever held the mic, both channels quiet for 60s prompts instead, tightened to 10s when the last tracked meeting app exits. Every raw meeting PCM chunk of both channels feeds a pure energy monitor via a single hook in sendMeetingAudio, sharing the echo-leak detector's thresholds through an alignment-safe RMS helper. Chunks are ignored until the controller session exists, since system audio starts streaming before the session is registered. Sleep gaps discard accumulated silence instead of stopping on wake, and a mic hold shorter than 30s no longer arms the mic-ignoring path on an in-person recording. Keep recording is now per episode with a 5-minute cooldown, the card explains which signal fired, and Settings → Meetings gains an "Auto-end forgotten recordings" toggle that takes effect immediately. * fix(meetings): Make auto-end recording fail safe * fix(meetings): Queue detections while a meeting recording session is live The engine's user-recording flag is shared with dictation, so a dictation that ends mid-meeting (or a cancel-hotkey press) clears it while the meeting recording is still live. From then on a detection — most plausibly the calendar reminder for a back-to-back meeting, which fires one minute before its start, right inside the auto-end countdown — bypassed the queue, and its prompt replaced the visible countdown card. The replacement path never notifies the controller, so the countdown ran on unseen and stopped the recording without any chance to keep it. Gate detections on the engine's own recording session as well, which nothing outside the recording lifecycle can reset: new detections are queued behind it like the user-recording gate, and a post-dictation cooldown flush holds the queue until the recording ends. This also stops the pre-existing "Take notes?" prompt that could appear during the user's own manual recording after a dictation. * fix(meetings): Persist the final transcript before awaiting the main-side stop Moving the final transcript save into the store's stop path (so auto-end and owner-loss stops persist without the notes view mounted) also moved it after `cleanup()` and the `meetingTranscriptionStop` round trip. The note editor swaps from live segments to the stored transcript the moment `isRecording` flips, so for the duration of that stop — up to a few seconds with a local Parakeet flush — the tail since the last 30s periodic save disappeared from the note and reappeared once the stop returned. On main the notes-view effect wrote it in the same commit as the state flip. Persist the captured segments before awaiting the stop; they are final once the session is released. Only a segment-less recording still waits for main's final transcript, as before. `persistFinalTranscriptAroundStop` replaces `buildFinalMeetingTranscript`, whose only caller was this path. * fix: clear meeting flag after recording session ends
OpenWhispr
The open-source and free alternative to WisprFlow and Granola.
Privacy-first voice-to-text dictation with AI agents, meeting transcription, and notes. Cross-platform for macOS, Windows, and Linux.
Website · Docs · Download · API · Changelog
OpenWhispr turns your voice into text, notes, and actions from your desktop. Press a hotkey, speak, and your words appear at your cursor. Choose between fully private offline transcription with local speech-to-text engines like Whisper and NVIDIA Parakeet — where your audio never leaves your device — or cloud processing for speed. No data collection, no telemetry, fully open source.
Download
| Platform | Download |
|---|---|
| macOS (Apple Silicon) | .dmg |
| macOS (Intel) * | .dmg |
| Windows | .exe |
| Linux | .AppImage / .deb / .rpm / .tar.gz |
* On Intel Macs, live speaker identification and voice fingerprinting are unavailable: they depend on ONNX Runtime, which stopped shipping macOS x86_64 binaries in 1.24. Meetings still record and transcribe normally, and notes search falls back to keyword matching instead of semantic search.
Features
- Voice dictation — global hotkey to dictate into any app with automatic pasting
- Dictation translation — dedicated hotkey to dictate in one language and paste the text in another
- AI agent — talk to GPT-5, Claude, Gemini, Groq, Tinfoil, OpenRouter, or local models with a named voice assistant
- Voice agent hotkey — dedicated hotkey that sends your dictation straight to your AI agent as a command, no wake word needed and no cleanup pass; edit highlighted text in place, or opt in to sending a screenshot of your current screen as context
- Meeting transcription — auto-detect Zoom, Teams, and FaceTime calls with live speaker diarization, voice fingerprinting, and Google, Microsoft, or Apple Calendar integration
- Local speaker diarization — on-device speaker labelling with voice fingerprint recognition across meetings, no cloud required
- Notes — create, organize, and search notes with folders, semantic search, cloud sync, and AI actions
- Team spaces & sharing — free for signed-in users; share notes on the web with link, domain, or invite-only visibility, and collaborate in team spaces with roles, invitations, and server-enforced membership
- Audio import — transcribe existing audio and video: drag in files, batch-upload, or paste a YouTube/audio URL, with optional speaker detection
- Local or cloud — your choice — all core features (transcription, AI reasoning, speaker diarization, semantic search) work with local models or cloud providers — including GPU-accelerated local Whisper on Metal, CUDA, and Vulkan (AMD/Intel)
- Enterprise controls — enforce organization policy, company SSO and SCIM, and centrally managed Amazon Bedrock or Azure OpenAI access without distributing cloud keys
- Public API & MCP — manage notes and transcriptions programmatically or connect your AI assistant via the MCP server
Quick start
git clone https://github.com/OpenWhispr/openwhispr.git
cd openwhispr
npm install
npm run dev
Requires Node.js 24+. See the full documentation for setup guides, platform-specific instructions, and build details.
Documentation
Visit docs.openwhispr.com for:
- Getting started
- Platform guides (macOS, Windows, Linux)
- API reference
- MCP server setup
- Troubleshooting
Repo examples:
- Custom ASR shim for Self-Hosted transcription against non-OpenAI-compatible ASR APIs
Tech stack
React 19, TypeScript, Tailwind CSS v4, Electron 41, better-sqlite3, whisper.cpp, sherpa-onnx, shadcn/ui
Star History
Sponsors
Neon is the serverless Postgres platform powering OpenWhispr Cloud.
Contributing
We welcome contributions. Fork the repo, create a feature branch, and open a pull request. See the contributing guide for development setup and guidelines.
License
MIT — free for personal and commercial use.
Acknowledgments
- OpenAI Whisper — speech recognition model powering local and cloud transcription
- whisper.cpp — high-performance C++ implementation for local processing
- NVIDIA Parakeet — fast multilingual ASR model
- sherpa-onnx — cross-platform ONNX runtime for Parakeet inference
- Hugging Face — model hub hosting Whisper, Parakeet, and embedding model weights
- llama.cpp — local LLM inference for AI text processing
- Electron — cross-platform desktop framework
- React — UI component library
- shadcn/ui — accessible components built on Radix primitives
- Neon — serverless Postgres powering OpenWhispr Cloud