backchannel
28 July 2026

Three degrees of local: what "self-hosted" actually means in a meeting assistant

"Private" is the most abused word in the meeting-AI category. It gets applied to products that record on your device and process in a vendor cloud, to products with good compliance badges and broad privacy policies, and to products that are private in exactly the way a sealed envelope handed to a courier is private. When I say Backchannel -- the open-source meeting assistant I build -- is self-hosted, I owe you something more precise than the word.

So here is the precise version: locality in this system is a spectrum with three positions, and you choose your position per component, in the admin panel. This post walks the pipeline and names exactly which bytes leave your machine at each position. Everything below is verifiable in the repository; I cite file paths instead of adjectives.

The pipeline, end to end

The browser captures microphone audio with getUserMedia, converts it to PCM16 16 kHz mono, and streams it over a WebSocket as binary frames with a 1-byte track prefix -- 0x00 for mic, 0x01 for tab/system audio (frontend/src/hooks/useAudioCapture.ts, backend/app/ws/audio_handler.py). Capturing the two tracks separately matters later: remote participants arrive on the system track and get their own speaker identities, so "who said what" survives even though everyone is coming out of one pair of speakers.

From there, four stages: voice activity detection and diarization, transcription, live interim captions, and the LLM agent layer. Each stage has a different locality story, and being honest about the seams is the point of this post.

Stage 1: diarization is always local, in every configuration

Speech segmentation and speaker attribution never touch a network. Silero VAD (ONNX) finds speech at a 0.6 threshold, segments run 750 ms to 15 s with a 600 ms silence gap, and each segment gets a speaker embedding from a WeSpeaker ResNet152-LM model, also ONNX, with Kaldi fbank features computed by kaldi-native-fbank with mean-only cepstral normalization -- matching the frontend the model was trained with (backend/app/services/speaker_diarizer.py, backend/app/config.py). Embeddings are matched against per-track speaker profiles at a 0.68 cosine-similarity threshold.

There is no "diarization API" in this system to route audio through. This was a deliberate constraint: the biometric part of the pipeline -- voice embeddings, the part that is literally a model of the people speaking -- runs on your hardware in all three configurations, full stop.

Call audio is also written to disk locally as per-segment WAV (backend/app/services/audio_store.py), which enables a feature I have not seen elsewhere in this category: POST /api/sessions/{id}/retranscribe replays any stored call through a different transcription model later, so you can test a new local model against your own real audio instead of against a benchmark.

Stage 2: transcription -- degree one and degree two

This is the first fork. Batch transcription routes through a factory (backend/app/services/local_transcriber.py): model ids beginning with local- run ONNX Whisper or Parakeet on your machine via onnx-asr, with weights downloaded once to the data directory; anything else wraps the diarized segment as WAV and sends it to Gemini on your API key.

Degree one, the default: local diarization, cloud transcription. Audio segments leave your machine, but to a provider you chose, on a key you hold, under that provider's API terms -- not to a meeting-AI vendor with its own retention and training policies layered on top.

Degree two: local transcription. No audio leaves the machine at all. The tradeoff is real and I will not launder it: hosted frontier models transcribe difficult audio -- crosstalk, accents, jargon -- better than compact local ONNX models. That is exactly what the re-transcription endpoint is for: run a meeting both ways and read the diff before you decide what your accuracy budget is.

Live interim captions follow the same fork. The default is a Gemini Live session acting as a silent listener that relays interim text (backend/app/services/gemini_live.py). Set the audio gateway model to local-parakeet-live and captions come from an on-device Parakeet captioner instead (backend/app/services/local_live_captioner.py) -- no cloud, works under Privacy First. It is experimental and CPU-heavy, and the app ships a fit test that projects whether your machine can sustain it, because "it works on my machine" is not a deployment plan.

Stage 3: the LLM agents -- degree three

The analysis layer is a crew of agents -- an analyst, a low-latency objection handler, a synthesizer, an opportunity specialist, a strategic signals agent, and a three-lens post-call briefing -- each with an editable model, prompt, and trigger. Agents consume transcript text, not audio. So in degree two, what leaves your machine is text, on your key.

Degree three closes that last seam. The backend speaks to any OpenAI-compatible server: register an endpoint (LM Studio, Ollama, vLLM, LiteLLM -- there are presets), it lists its models, and every served model becomes a first-class entry in the model registry with the id endpoint:<slug>:<model>, selectable in every model picker next to the cloud options (backend/app/services/custom_endpoints.py, backend/app/services/llm_endpoint.py). Point the agents at a model served on your own box or your LAN, use local ONNX transcription, and the entire pipeline -- capture, diarization, transcription, live captions, agents -- runs with no API key from anyone and no packet leaving your network.

Privacy First mode is the enforcement mechanism rather than a promise: it locks every model picker to models flagged as running locally, and it admits self-hosted endpoints on loopback, private-network addresses, or LAN hostnames (frontend/src/lib/modelOptions.ts). Flip it on and a cloud model is not a worse choice; it is not a choice.

What I am not claiming

Honesty about the boundary is worth more than the boundary itself, so:

  • Degree one sends audio to your model provider, and degrees one and two send transcript text to it. If your threat model includes your own API provider, only degree three answers it.
  • Local model quality is your problem to evaluate. A 7B instruct model will not write the same objection analysis a frontier model writes. The per-agent model picker exists precisely so you can spend cloud budget where it matters and keep the rest local -- but I will not pretend the outputs are interchangeable.
  • There are no user accounts or authentication in the application. It is a single-operator tool designed to sit behind your own network boundary, not on the public internet. Provider keys are encrypted at rest (Fernet, with a master key in the data directory), but access control is your reverse proxy's job, not the app's.
  • There are no compliance badges. No SOC 2, no ISO, no HIPAA. What I can offer instead is an MIT-licensed codebase where every claim in this post is a file path you can read. For some reviewers that is stronger than an attestation; for procurement it is usually weaker. Know which reviewer you are.

Why the spectrum, instead of just shipping degree three

Because the honest engineering answer is that the degrees trade accuracy against locality, and that trade belongs to you, not me. A sales team that wants maximum insight quality with audio kept on-premises runs degree two. A therapist, a lawyer, or anyone whose transcripts are the sensitive artifact runs degree three and accepts local-model output. A homelab user benchmarking Parakeet against Gemini on their own standup recordings runs all three in an afternoon through the re-transcription endpoint.

A hosted product has to pick one position on this spectrum for all of its customers, and its incentives push it cloudward. A self-hosted one can hand you the slider. That -- not a bot-free gimmick, not a compliance page -- is what "self-hosted" actually buys you.

The code is at github.com/talberthoule/backchannel under MIT, and it runs with docker compose up --build. If you try degree three with a local model I have not tested, the issue tracker is where I would genuinely like to hear about it.

Meeting AI that runs on your hardware

Self-hosted, open source, MIT licensed. No bot in the room, no audio in anyone's cloud.