This release includes 1 security fix for security teams reviewing exposed deployments.
Topics
+5 more
Affected surfaces
ReleasePort's take
Moderate signalThe Fono v0.10.0 release locks the transcription history database (history.sqlite) and log file (/var/log/fono.log) to owner‑only permissions on shared machines.
Why it matters: Restricts access to sensitive transcription data, reducing risk of unauthorized reads or tampering; applies immediately after upgrade.
Summary
AI summaryUpdates https://bogdan.nimblex.net/programming/2026/06/10/making-local-llm-fast.html, American, and British across a mixed release.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Critical |
Locks transcription history DB and log file to owner-only permissions on shared machines. Locks transcription history DB and log file to owner-only permissions on shared machines. Source: llm_adapter@2026-06-12 Confidence: low |
— |
| Feature | Medium |
Local AI cleanup now streams cleaned text word‑by‑word as it thinks. Local AI cleanup now streams cleaned text word‑by‑word as it thinks. Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Feature | Medium |
Offline Piper/Kokoro TTS ships in the default binary (42 voices across 38 languages). Offline Piper/Kokoro TTS ships in the default binary (42 voices across 38 languages). Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Performance | Medium |
Embedded llama.cpp engine reuses prompt checkpoints for faster repeat dictations. Embedded llama.cpp engine reuses prompt checkpoints for faster repeat dictations. Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Bugfix | High |
Prevents Gemma model cleanup from looping indefinitely and injecting garbage. Prevents Gemma model cleanup from looping indefinitely and injecting garbage. Source: llm_adapter@2026-06-12 Confidence: low |
— |
| Bugfix | Medium |
Local TTS now selects the correct voice per utterance language, fixing mismatched phonemes. Local TTS now selects the correct voice per utterance language, fixing mismatched phonemes. Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Bugfix | Medium |
Fixes phonemization failure for US English TTS voice (en_US-amy-medium). Fixes phonemization failure for US English TTS voice (en_US-amy-medium). Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Bugfix | Medium |
Correctly handles Romanian comma‑below characters (ș, ț) in local TTS. Correctly handles Romanian comma‑below characters (ș, ț) in local TTS. Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Bugfix | Medium |
Routes local polish backend through embedded engine instead of silently falling back to Ollama HTTP server. Routes local polish backend through embedded engine instead of silently falling back to Ollama HTTP server. Source: llm_adapter@2026-06-12 Confidence: high |
— |
| Bugfix | Medium |
Reduces hands‑free recording auto‑stop silence window from 5 s to 3 s for faster commit. Reduces hands‑free recording auto‑stop silence window from 5 s to 3 s for faster commit. Source: llm_adapter@2026-06-12 Confidence: low |
— |
Full changelog
Faster local AI, local text-to-speech out of the box, and cleanup that types
as it thinks. The embedded engine now reuses prompt checkpoints so warm
dictations and follow-up assistant turns stay quick, offline Piper/Kokoro TTS
ships in the default binary, and local AI cleanup streams into the cursor
word-by-word instead of making you wait for the whole pass. Plus the usual
round of fixes — Gemma cleanup no longer loops, and history/log files are
locked down on shared machines.
Added
- Local AI cleanup now types as it thinks. When cleanup runs on the
embedded local model, the cleaned text streams into the cursor word by
word as the model decodes it, instead of making you wait for the whole
pass before anything appears. On a long dictation the first words land in
about one to three seconds rather than after the full seven-to-twenty-second
decode, then keep flowing continuously. All of the existing safety checks
still run on the first sentence before a single character is typed — a
clarifying-question, degenerate, or wrongly-translated cleanup still falls
back to your raw transcript with nothing typed. It applies only to the
local backend (cloud cleanup is already sub-second and one-shot) and turns
itself off automatically for short utterances and clipboard-fallback
sessions. On by default; set[polish].stream_injection = falseto keep
the old wait-for-the-whole-thing behaviour. - Local AI got noticeably snappier on repeat use. The embedded
llama.cpp engine now keeps reusable checkpoints of the prompts it has
already processed (the cleanup instructions, the assistant's system
prompt, your running conversation), so warm dictations and follow-up
assistant turns skip re-crunching all of that and only process what's
new. Time-to-first-token stays flat as a conversation grows instead of
climbing with every turn; restoring a saved checkpoint takes tens of
milliseconds where a cold re-read of a long conversation took seconds.
Cleanup also gets a per-app checkpoint, so dictating into your editor,
browser, or terminal each reuses its own warmed-up state. The how and the
measured numbers are written up in
Making local LLM fast. - English dictation read-back now uses Kokoro, a higher-quality local
voice, while every other language keeps using Piper. Four English voices
ship —af_heart(the default),af_bella,af_nicole(American) and
bf_emma(British) — all sharing one model, so picking a different one is
a tiny download. The model and voices fetch on demand like every other
voice, and the binary's runtime dependency set is unchanged (the
statically linked runtime grows ~0.8 MiB, well under the size budget). - Local text-to-speech is now built into the shipped binary by default. The
cpuandgpudownloads do offline Piper TTS out of the box (42 voices
across 38 languages); pick it withfono use tts local. The statically
linked ONNX Runtime adds ~2 MiB and keeps the binary's runtime dependency
set at the same four-entry glibc allowlist (measured 24.57 MiB,cpu).
Fixed
- Local cleanup with a Gemma model no longer loops or runs away. Three
compounding bugs made the embedded engine repeat a (correctly cleaned)
sentence until a token cap — turning a 1-second cleanup into a 25-second
one that injected garbage. The engine now renders Gemma's own prompt
format instead of assuming ChatML, stops generation on any
control/end-of-turn token (model-agnostic, so models with non-standard
vocabularies stop correctly too), and applies a repetition penalty to its
own output so near-echo cleanups can't lock into a verbatim loop. - Privacy hardening on shared machines. The transcription history
database (history.sqlite) is now clamped to owner-only permissions
(0600) every time it is opened — it holds everything you have ever
dictated and was previously created with the default umask (typically
world-readable). And/var/log/fono.logis no longer created
world-writable (0666):fono installnow creates it owned by the
installing user with mode 0600, so other local users can neither read
usage details (focused-window classes/titles) nor poison/truncate the
log. Fono processes run by other users fall back to/dev/nullfor
logging, as before. - Local LLM cleanup (
[polish].backend = "local") now runs the embedded
llama-cpp-2engine on a local GGUF, as intended — it no longer silently
routes any Gemma-named model to an Ollama HTTP server. The old behaviour
meant the default local setup (modelgemma-4-e2b) POSTed to
http://localhost:11434, 404'd on a model Ollama didn't have, and fell back
to injecting the raw transcript with no notification — so cleanup appeared to
do nothing. The factory'sis_gemma_modelspecial-case has been removed; a
missing local GGUF now fails loudly with a one-shot notification pointing at
fono models install <model>, and the setup wizard's "local polish" choice
no longer writes a stale Ollama[polish.cloud]block. An Ollama /
OpenAI-compatible server is reachable only via the explicit
backend = "ollama"config. As a consequence of routing polish through the
embedded engine, both the cleanup and voice-assistant paths now share a
single process-wideLlamaBackend(infono-core); previously each crate
initialised llama.cpp independently, and once polish also went embedded the
second initialiser panicked — surfacing asllama-local mutex poisonedon
whichever path ran second (typically the assistant stream after a dictation
turn). - Local TTS now speaks each reply in the matching voice. Previously a bilingual
user heard every reply in their primary language's voice — a Romanian reply
read aloud by the English voice (wrong phonemes, wrong accent). The local
backend now picks the voice per utterance from the language of the text it is
about to speak, identified against the configuredgeneral.languages, and
loads each language's voice on demand. Identifying the language from the text
itself (rather than from the speech recogniser's detected input language)
is what makes a Romanian answer to an English question come out in the
Romanian voice. The speech-recogniser language is used only as a fallback for
text too short to fingerprint. An explicit[tts.local].voicestill pins one
voice for everything. - Local TTS for the US English voice (
en_US-amy-medium) no longer fails
phonemization. Its.onnx.jsondeclares espeak voiceen-us, which had no
dictionary in the catalog; it now folds onto the sharedenbase/en_dict
(same as the Britishen-gb-x-rpvoice), so the on-demand dictionary
download resolves instead of warning. - Local TTS no longer mangles Romanian words containing the comma-below letters
ș/ț. The vendored pure-Rust espeak-ng port truncated a word at the first
such letter (Ploieșticame out as "Ploie") or dropped it entirely (țara
was silent); these comma-below codepoints are now folded onto their cedilla
equivalents (ş/ţ) before phonemization, as the upstream C library does
internally.
Changed
- Hands-free recording stops a little sooner. The default auto-stop
silence window dropped from 5 s to 3 s, so dictation commits faster
after you stop talking. Set[audio].auto_stop_silence_msto keep a
longer pause budget.
Full Changelog: https://github.com/bogdanr/fono/compare/v0.9.0...v0.10.0
Security Fixes
- Privacy hardening: history.sqlite and fono.log now have owner‑only permissions (0600) on shared machines.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About Fono
All releases →Related context
Related tools
Beta — feedback welcome: [email protected]