Local-first speech-to-text CLI.
sotto captures microphone audio, streams to a local ASR backend (Riva by default), assembles transcript text, and commits output to the clipboard with optional paste dispatch.
- single-process CLI (no daemon)
- local-first by default (localhost Riva endpoints)
- explicit state machine (
toggle,stop,cancel) - deterministic config + observable runtime logs
- single-instance command coordination via unix socket
- audio capture via PipeWire/Pulse
- streaming ASR via NVIDIA Riva gRPC
- transcript normalization + sentence capitalization + optional trailing space
- output adapters:
- clipboard command (
clipboard_cmd) - optional paste command override (
paste_cmd) - default Hyprland paste path (
hyprctl sendshortcut) whenpaste_cmdis unset
- clipboard command (
- indicator backends:
hyprnotificationsdesktop(freedesktop notifications, e.g. mako)
- embedded cue WAV assets for start/stop/complete/cancel (not user-configurable)
- built-in indicator localization scaffolding (English catalog currently shipped)
- built-in environment diagnostics via
sotto doctor
sotto is currently optimized for Wayland + Hyprland workflows.
- default paste behavior uses
hyprctl doctorcurrently checks for a Hyprland session
You can still reduce Hyprland coupling by using:
indicator.backend = desktoppaste_cmd = "..."(explicit command override)
nix build 'path:.#sotto'
nix run 'path:.#sotto' -- --helpjust tools
go test ./apps/sotto/...
go build ./apps/sotto/cmd/sottosotto doctor
sotto toggle # start
sotto toggle # stop + commitCore commands:
sotto toggle
sotto stop
sotto cancel
sotto status
sotto devices
sotto doctor
sotto versionConfig resolution order:
--config <path>$XDG_CONFIG_HOME/sotto/config.jsonc~/.config/sotto/config.jsonc
Compatibility note:
- if the default
.jsoncfile is missing, sotto will fall back to legacyconfig.confautomatically.
See full key reference and examples in:
Required local gate before hand-off:
just ci-check
nix build 'path:.#sotto'Manual/local runtime checklist: