Local-first, hold-to-talk voice dictation for macOS. Hold a hotkey, speak, release — your speech is transcribed and inserted at the cursor in any app. Similar in spirit to Wispr Flow, but open and self-hosted: the speech-to-text and optional LLM cleanup run locally by default, and both endpoints are configurable so you can move processing to another machine (e.g. an NVIDIA DGX Spark) by changing a URL.
Early development. See PLANNING.md and
docs/superpowers/specs/2026-06-01-murmur-design.md.
- Hold the hotkey (default: Right Option ⌥) and speak — or, in toggle mode, tap once to start and tap again to finish (comfortable for long notes).
- On release/stop, Murmur sends the recorded audio to the speech-to-text engine and (optionally) runs an LLM cleanup pass.
- The text is inserted at your cursor in the frontmost app.
- Speech-to-text:
whisper-large-v3-turbovia whisper.cpp (Metal-accelerated, multilingual). - Optional LLM cleanup:
Qwen2.5-7B-Instructvia Ollama (OpenAI-compatible API).
- Apple Silicon Mac, macOS 14+ (developed targeting an M2 Pro, 32 GB).
- A Swift toolchain (full Xcode, or Command Line Tools — see build note below).
- macOS permissions: Microphone, Accessibility, Input Monitoring.
- Ollama installed if you want the LLM cleanup pass.
# 1. One-time: build the whisper.cpp server + download the model
# (installs cmake via Homebrew if missing; model is ~547 MB)
./Scripts/setup.sh
# 2. (optional) LLM cleanup backend
ollama pull qwen2.5:7b
# 3. Build the app bundle and launch
./Scripts/run.sh # builds Murmur.app and opens itThe individual steps (build-whisper.sh, fetch-model.sh) can also be run directly;
fetch-model.sh accepts MODEL_FILE/MODEL_URL overrides to use a different model.
Scripts/make-app.sh produces a real Murmur.app with an Info.plist (so macOS will
grant the microphone permission — a bare swift run binary cannot request it). Grant the
three permissions when prompted (or in System Settings → Privacy & Security), then hold
Right Option and talk.
The standard build is SwiftPM (swift build), used automatically by make-app.sh. If
your Command Line Tools install is affected by the known duplicate-SwiftBridging
modulemap bug (every Foundation import fails to compile), Scripts/build-swiftc.sh
builds without SwiftPM and auto-applies a VFS-overlay workaround — no system files are
modified. make-app.sh falls back to it automatically.
Murmur needs three macOS permissions — Microphone, Accessibility, and Input
Monitoring. macOS (TCC) ties each grant to the app's code signature: when you grant a
permission it records the app's designated requirement (DR) and re-checks it on every
launch. make-app.sh signs the bundle automatically and picks the strongest mode
available:
- Ad-hoc with a stable DR — the default, zero setup. With no code-signing certificate
on the machine, the app is ad-hoc signed but pinned to an explicit DR of
identifier "io.github.jvr0x.murmur". That DR does not change between rebuilds, so your permission grants survive updates. Caveat: an identifier-only DR is satisfied by any local binary that claims this bundle identifier — there is no cryptographic anchor. Acceptable for a personal dev build. - Certificate-anchored DR — recommended. Run
./Scripts/make-signing-cert.shonce to create a self-signed code-signing certificate in your login keychain. Afterwardsmake-app.shauto-detects it and signs with it, producing a DR of the formidentifier "…" and certificate leaf = H"…"— stable across rebuilds and cryptographically anchored to your certificate. The script is interactive: it asks for your login-keychain password (so codesign can use the key without prompting on every build) and macOS may show a one-time trust dialog. Override the identity name withMURMUR_SIGN_IDENTITYif you like (it must match for both scripts).
Why permissions used to break on every rebuild. The old signing produced an implicit
DR of cdhash H"…" — a hash of that exact build. Every rebuild changed the hash, so the
DR macOS had stored no longer matched, macOS treated the new build as a different app, and
all three grants silently went stale. The stable DR above fixes that.
Switching signing modes re-grants once. Moving from ad-hoc to the certificate (or the reverse) changes the DR one final time, so macOS asks for the three permissions once more right after the switch. From then on the DR is constant and grants persist.
Update in place, don't delete-then-copy. Because TCC matches on the bundle at its
install path, make-app.sh rebuilds Murmur.app in place without removing the directory.
If you keep a copy elsewhere (e.g. ~/Applications), overwrite it rather than deleting
and re-copying, so the existing TCC entry keeps matching:
ditto Murmur.app ~/Applications/Murmur.app # overwrites in placeA delete-then-copy can drop the TCC entry and force an unnecessary re-grant.
Settings (menu-bar icon → Settings) let you change the hotkey, STT/LLM endpoints, models, and the cleanup prompt. To use a remote server, set the STT backend to "OpenAI-compatible" with the server URL and/or point the LLM base URL at the remote host.
The LLM Cleanup section has a Provider picker that prefills the base URL for Ollama
(http://localhost:11434/v1) or LM Studio (http://localhost:1234/v1); choose
Custom to point anywhere else (e.g. a model server on your Spark). The URL stays the
source of truth, so editing it just flips the picker to the matching provider.
Pick a ready-made preset (Right Option ⌥, Right Command ⌘, fn/Globe, F20, F13, ⌃ + Space, ⌥ + Space) or record your own: click Record and press any key, modifier, or combo — modifiers plus a key (⌃ + Space), or modifiers only (⌘ + ⌥). Esc cancels. Changes apply immediately, no relaunch.
Activation chooses how the hotkey drives a dictation:
- Hold to talk (default) — press and hold while speaking, release to transcribe.
- Tap to start / stop — tap once to start recording, tap again to finish.
MIT. See LICENSE.