- niri: index/config-tree rework, displays.md desk-profile rewrite, tips/action-reference/default-bindings refresh (26.04), generated keybindings.md (119 bindings) - noctalia-shell.md: settings.toml lives in ~/.local/state, taskbar widget, v5 keybind table (Mod+D/Mod+V/Alt+Tab) - handy.md: NEW; live nested store shape, fleet endpoint, known thinking-model empty-content breaker + verified cure - log.md: DE redesign entry + pam_faillock sudo-lockout incident
6.9 KiB
type, title, description, resource, tags, timestamp, verified
| type | title | description | resource | tags | timestamp | verified | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| reference | Handy | Offline speech-to-text transcription with WebKitGTK backend and Tauri v2 UI. | https://github.com/cjpais/Handy |
|
2026-10-09 | 2026-10-09 |
Handy
Handy is an offline speech-to-text (STT) transcription tool built with Tauri v2 and WebKitGTK. It runs entirely offline, supports multiple GPU backends, and provides real-time transcription with optional post-processing.
Installed version: 0.9.6-1 (local/handy-bin, /usr/bin/handy) — 0.9.8 upgrade pending: yay -Syu handy-bin (root work is operator-owned; agents never sudo).
Upstream latest: 0.9.8 (as of 2026-10-03)
Binary: /usr/bin/handy
Config: ~/.config/com.pais.handy/ (upstream docs) / ~/.local/share/com.pais.handy/settings_store.json (live observed)
Architecture
Handy uses Tauri v2 for its UI (WebKitGTK for rendering) and a native Rust backend for STT processing. On Wayland, it requires compositor-level global shortcuts.
Live settings snapshot (blankpad):
| Setting | Value |
|---|---|
paste_method |
none |
copy_to_clipboard |
true |
push_to_talk |
true |
bindings |
transcribe=ctrl+space, transcribe_with_post_process=ctrl+shift+space, cancel=escape |
keyboard_implementation |
handy_keys |
overlay |
bottom (live), true (config) |
model |
handy-computer/canary-180m-flash-gguf (Q8_0) |
transcribe_gpu |
vulkan (PCI 07:00.0) |
autostart_enabled |
false (niri-managed) |
experimental |
true |
filler_word_removal |
true |
post_process_enabled |
true (fleet-wired) |
clipboard_handling |
copy_to_clipboard |
provider |
custom (base_url=http://192.168.0.143:7771/v1, model=frank-secondary) |
prompt |
default_improve_transcriptions |
Configuration
Config Location
Upstream docs: ~/.config/com.pais.handy/
Live observed: ~/.local/share/com.pais.handy/settings_store.json
Both locations may contain settings; the live data directory is the authoritative runtime store.
Live Settings (blankpad)
The store is nested: everything lives under a top-level settings object.
Post-processing wiring (applied 2026-10-09) touches five keys plus the
custom entry inside post_process_providers:
{ "settings": {
"post_process_enabled": true,
"post_process_provider_id": "custom",
"post_process_models": { "custom": "frank-secondary", "openai": "", "...": "" },
"post_process_selected_prompt_id": "default_improve_transcriptions",
"post_process_providers": [
{ "id": "custom", "base_url": "http://192.168.0.143:7771/v1",
"allow_base_url_edit": true, "models_endpoint": "/models", "...": "" }
],
"post_process_api_keys": { "custom": "", "...": "" },
"autostart_enabled": false,
"push_to_talk": true,
"paste_method": "none",
"clipboard_handling": "copy_to_clipboard",
"keyboard_implementation": "handy_keys",
"overlay_position": "bottom",
"overlay_style": "live"
} }
Repo-side merge patch + apply procedure:
dotfiles/zz-blankpad/.local/share/com.pais.handy/ (README.md).
Command-Line Interface
Startup Flags
handy --start-hidden # Start without showing the UI
handy --no-tray # Hide the system tray icon
handy --debug # Enable debug mode (Ctrl+Shift+D)
Remote Control
Send commands to a running instance:
handy --toggle-transcription # Toggle transcription on/off
handy --toggle-post-process # Toggle post-processing on/off
handy --cancel # Cancel the current transcription
Wayland Integration
Global Shortcuts
On Wayland, global shortcuts must be defined in the compositor (niri), not in Handy itself.
File: ~/.config/niri/cfg/keybinds.kdl
// Live (cfg/keybinds.kdl):
Mod+Shift+Space hotkey-overlay-title="Handy: toggle post-process recording" allow-when-locked=true { spawn-sh "handy --toggle-post-process"; }
// Transcription itself stays on Handy's own ctrl+space (keyboard_implementation="handy_keys").
Text Input
Requirement: wtype or dotool for text input (NOT installed on blankpad).
Current config: paste_method=none + manual Ctrl+V paste.
Overlay
Default: Disabled on Linux (can steal focus and break paste).
Live config: mode=bottom, live=true.
Troubleshooting
WebKit Renderer Issue
Symptom: Blank or corrupted rendering.
Fix:
export WEBKIT_DISABLE_DMABUF_RENDERER=1
handy
SIGUSR1 Crash
Trap: The upstream SIGUSR1 handler was REMOVED (WebKitGTK now owns it).
# WRONG (may crash the app):
pkill -USR1 handy
# CORRECT (toggle transcription):
pkill -USR2 -n handy
# or use CLI:
handy --toggle-transcription
Debug Mode
handy --debug
# or press Ctrl+Shift+D in a running instance
Dependencies
| Package | Purpose |
|---|---|
handy |
Main application |
tauri-apps |
Tauri v2 runtime |
webkit2gtk |
WebKitGTK rendering |
gtk-layer-shell |
Wayland layer-shell support |
vulkan |
GPU-accelerated STT |
ffmpeg |
Audio processing |
Fleet LLM Endpoint
Post-processing pipeline: handy → http://192.168.0.143:7771/v1, model id
frank-secondary (the served id; harness callsign for the same unit is
frank-zealot — same llama.cpp server, also the fleet compaction endpoint).
Endpoint configuration:
- Provider:
custom(the one preset withallow_base_url_edit: true) - Base URL:
http://192.168.0.143:7771/v1 - Model:
frank-secondary - Prompt:
default_improve_transcriptions
Known issues
Thinking-model empty content (2026-10-09, open). frank-secondary is a
thinking model. Default chat completions return content: "" with the answer
trapped in reasoning_content — Handy reads content, so post-processed
transcripts arrive empty. Verified per-request cure:
"chat_template_kwargs": {"enable_thinking": false} (probe below returns
cleaned text in content). Handy has no field to send extra body keys, and
the cure CANNOT be set server-global on :7771 without degrading fleet
compaction (same server). Proposed fix (not yet implemented): a dedicated
no-think unit/port for Handy traffic, e.g. llama-server on :7772 with
thinking disabled at the template level, then repoint custom.base_url.
curl -s http://192.168.0.143:7771/v1/chat/completions -d '{
"model": "frank-secondary",
"messages": [{"role":"user","content":"Clean this transcript: twenty five items cost ten dollars um ok."}],
"max_tokens": 200,
"chat_template_kwargs": {"enable_thinking": false}}'
See noctalia-shell for IPC integration patterns.
See Also
- noctalia-shell — Desktop environment integration
- niri — Compositor configuration
handy --help— CLI reference