WinSTT logoWinSTT
Architecture

IPC — Commands

How the renderer calls into Rust via Tauri commands and tauri-specta bindings

The renderer talks to the Rust backend through Tauri commands — typed request/response functions. There is no WebSocket and no separate process; an invoke is a direct call into Rust on the Tauri runtime. Push notifications go the other way as events.

~200
Tauri commands
100%
Through generated bindings
0
WebSockets / IPC processes
1 call
Renderer → Rust hop

How a call reaches Rust

Commands are plain Rust functions annotated #[tauri::command] #[specta::specta], collected in lib.rs::make_specta_builder() via collect_commands![...] and mounted as the app's invoke_handler. tauri-specta reads those signatures and generates a typed TypeScript client.

renderer calls commands.winsttSetParameter(parameter, value)  (generated src/bindings.ts)
  → TAURI_INVOKE("winstt_set_parameter", …)
  → invoke_handler → winstt::commands::dictation::winstt_set_parameter()
  → Result<T, String>  →  { status: "ok", data } | { status: "error", error }

Two pieces make this work:

src/bindings.ts

Generated by tauri-specta. Every command is a typed wrapper returning { status: "ok", data } | { status: "error", error }. A #[test] export_bindings regenerates it so CI can git diff --exit-code and fail if the file drifts from the live command registry.

collect_commands![]

In src-tauri/src/commands_registry.rs — the single list mounted as the app's invoke_handler. A completeness guard test fails if a #[tauri::command] function is missing from it, so the generated bindings can never silently drift from the live registry.

One typed path, no string channels

Every renderer call site calls commands.* directly, so a signature change in Rust breaks the TypeScript build. The old string-channel funnel — ipc-channels.tsipc-transport.ts COMMAND_INVOKERSnative-bridge-adapter.ts ROUTE, all fronted by a window.nativeBridge polyfill — was deleted in 720890c6. src/shared/api/native-boundary.test.ts now scans every non-test file under src/ and fails the build if any of it comes back.

Command Catalog

~200 commands live in src-tauri/src/winstt/commands/ (plus a handful of legacy commands in src-tauri/src/commands/), grouped by concern:

Command groups in src-tauri/src/winstt/commands/, by concern.
GroupModuleRepresentative commands
Dictationdictation.rswinstt_set_model, winstt_set_parameter, winstt_call_method
Hotkeyhotkey.rshotkey_register, hotkey_start_recording, hotkey_stop_recording
STT modelsstt.rs, runtime.rslist_models, list_models_with_state, picker_quantizations_for, set_custom_model
Downloadsdownload.rspredownload_quant, download_pause/resume/cancel_quant, delete_model_quantization
TTStts.rstts_speak, tts_cancel, tts_list_voices, tts_predownload_model
LLMllm.rsprocess_text, process_transform, scan_ollama_models, ollama_pull
Cloud STTcloud_stt.rs, verify.rsverify_cloud_stt_credential, verify_integration_credential
Settingssettings.rs, secret_storage.rswinstt_get_settings, winstt_set_settings (secrets sealed enc:v1:)
Audio / systemaudio_devices.rs, runtime.rsget_audio_devices, gpu_get_info, get_runtime_info
Historyhistory.rshistory_list, history_load_audio, align_words
Files / listenfile_transcribe.rs, listen.rsfile_transcribe_enqueue, start_listen, stop_listen
Windows / traywindows.rs, tray_menu.rsopen_window, resize_window, show_tray_menu
Diagnosticsdiag.rs, about.rsdiag_save_bundle, about_get_app_info, get_notices

Failure handling is tiered

invoke() mutations on the critical path (download/delete/reload, settings save) re-throw on error; high-blast-radius reads log and fall back. The adapter logs channel + argument digest

  • error before any fallback, so a swallowed failure is still observable in the console.

On this page