Skip to content

Fono

v0.10.0 Security

This release includes 1 security fix for security teams reviewing exposed deployments.

✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →
This release patches 1 known CVE

Topics

assistant dictation linux llm local-first rust
+5 more
speach-to-text stt vulkan whisper wyoming

Affected surfaces

auth deps

ReleasePort's take

Moderate signal
editorial:auto 1mo

The 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 summary

Updates https://bogdan.nimblex.net/programming/2026/06/10/making-local-llm-fast.html, American, and British across a mixed release.

Changes in this release

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 = false to 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
    cpu and gpu downloads do offline Piper TTS out of the box (42 voices
    across 38 languages); pick it with fono 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.log is no longer created
    world-writable (0666): fono install now 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/null for
    logging, as before.
  • Local LLM cleanup ([polish].backend = "local") now runs the embedded
    llama-cpp-2 engine 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 (model gemma-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's is_gemma_model special-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-wide LlamaBackend (in fono-core); previously each crate
    initialised llama.cpp independently, and once polish also went embedded the
    second initialiser panicked — surfacing as llama-local mutex poisoned on
    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 configured general.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].voice still pins one
    voice for everything.
  • Local TTS for the US English voice (en_US-amy-medium) no longer fails
    phonemization. Its .onnx.json declares espeak voice en-us, which had no
    dictionary in the catalog; it now folds onto the shared en base/en_dict
    (same as the British en-gb-x-rp voice), 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ști came 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_ms to 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

Track Fono

Get notified when new releases ship.

Sign up free

Related context

Beta — feedback welcome: [email protected]