Consensus
A privacy-first macOS transcription app that runs two engines on the same audio and flags exactly the spots where they disagree. Now with a headless mode that runs the whole pipeline unattended.
The Use Case
Litigators record things constantly: depositions in unofficial copies, witness interviews, client calls, internal strategy sessions, motion-hearing audio. Most of those recordings are confidential — sometimes privileged. Uploading them to a cloud transcription service to get a rough transcript before the certified version arrives is a privacy posture that does not survive contact with a thoughtful client.
So the audio either gets typed up by a paralegal or it doesn't get transcribed at all. Both are bad answers. Consensus is the third one — a fully local Mac app that runs everything on your own machine and exports a transcript in court-reporter PDF format on the way out.
The Problem With Single-Pass Transcription
Every consumer transcription app runs one engine, one pass, and gives you one result. If it misheard something, you have no way of knowing unless you listen to the entire recording yourself. That defeats the purpose.
Professional court reporting solves this by having two independent transcribers work the same recording and a third person reconcile the differences. The disagreements are exactly the spots where the audio is ambiguous — the places a human should actually listen to. No consumer software does this.
Consensus runs two different speech-to-text engines on the same audio and compares the results. Where they agree, you can trust the transcript. Where they disagree, the app flags those spans and lets you resolve them inline — select the better version, accept, move on. You end up with a verified transcript built from multiple independent readings of the same audio. Otter, Rev, Descript, Trint, MacWhisper, Sonix — all single-engine, single-pass.
What's Inside
Fully Local
Nothing leaves your machine. No cloud services, no API keys, no accounts. Audio stays on your Mac and never touches a server.
Speaker Identification
Automatic speaker diarization clusters voices so you know who said what. Rename speakers after transcription to match real names.
Quality Metrics
Word confidence, segment confidence, diarization quality, compression ratio. Automatic risk flagging highlights the spots most likely to contain errors.
7 Export Formats
Plain text, Markdown, JSON, SRT subtitles, RTF, Word, and a legal PDF in court reporter format: 25 lines per page, Courier 12pt, line numbers.
New in v2.0.0: Headless Mode
The app solved transcription. It did not solve the part where I have to remember to open it. Every recording still needed me to sit down, import a file, and wait — which meant recordings piled up untranscribed, which is the same problem I started with.
So v2.0.0 splits the project into a core library with two front ends: the Mac app, and a consensus command-line binary that does one thing — one audio file in, a diarized transcript out, no window, no prompts, no supervision. It runs from a launch agent at 3am if that's when the file lands.
That turned Consensus into something a script can build on. Mine watches a folder my phone syncs to, transcribes whatever appears, and files the result into my notes with a summary. The transcription step is now the boring part, which is what I wanted.
The headless engine runs VibeVoice ASR (4-bit MLX) with FluidAudio for diarization, and writes a versioned JSON schema alongside readable Markdown. Outputs are written to a temp file and renamed into place, so a watcher never sees a half-written transcript. It exits with distinct codes for "already processed", "input unreadable", and "transcription failed", so the calling script can tell the difference between a problem and a no-op. Speaker labels stay SPEAKER_A, SPEAKER_B — the binary never guesses at names, because a confident wrong name is worse than no name.
The Process
Consensus started as a Python prototype with a Gradio web UI. It worked, but it had all the friction of a Python ML project: heavyweight dependencies, environment management, and a web interface that felt wrong on a Mac. The native Swift rebuild was about making it something I'd actually use day to day.
This project has been more challenging than I expected. Single-pass transcription works well, but the multi-engine pipeline — figuring out the right workflow, the comparison algorithm, the user interface for resolving disagreements — has taken a lot of iteration. Speaker diarization has been the toughest piece: getting consistent, accurate speaker labels from local models is an active area of research across the field, and my experience reflects that.
The project has also involved more research than anything else I've built. Evaluating transcription engines, studying confidence-weighted word alignment (ROVER), testing multiple diarization approaches at different thresholds, reading papers on error rates, tracking new model releases. Local ML is moving fast, and keeping up has been part of the work.
It's improving with each iteration. The pipeline has been overhauled twice, the reconciliation interface redesigned from a 300-row grid into an inline flag system, and the diarization post-processing now runs three correction passes. Getting it right is taking time, but the bones are solid and the single-pass mode already does what I originally needed.
The v2.0.0 split was the change that made the rest of it useful. Pulling the core out from under the UI meant the app and the CLI run identical code, so there is no second implementation to keep honest — and it forced the pipeline to stop assuming a human was watching it. Most of the robustness work in this release came from that assumption being removed: atomic writes, meaningful exit codes, and a doctor command that tells you exactly which dependency is missing and where it looked.
Built With
Swift 6 with strict concurrency. SwiftUI on macOS 15+. WhisperKit for primary transcription in the app (5 model sizes from 75MB to 3GB), with FluidAudio's Parakeet v3 as the second engine for Deep Review. The headless CLI runs VibeVoice ASR as a 4-bit MLX model. FluidAudio and SpeakerKit for diarization throughout. ZIPFoundation for Word document generation. All Swift dependencies via Swift Package Manager.