The stt-daemon CLI provisions the virtual sink, runs the daemon, and lists devices. Every subcommand accepts the common overrides --config, --device, --backend, --model, --language, and -v/--verbose.
The stt-daemon CLI supports these subcommands. Every subcommand also accepts the
common overrides --config, --device, --backend, --model, --language, and
-v/--verbose (for example stt-daemon setup --device ...).
setup — Create virtual sink and loopbacksInteractive setup (shows device picker):
uv run stt-daemon setup
Non-interactive setup (specify sources directly):
uv run stt-daemon setup --sources "alsa_input.usb-Foo,alsa_output.pci-0000.monitor"
This creates:
VirtualMicSink)VirtualMic)run — Start the daemon (auto-provisions if needed)uv run stt-daemon run
If no saved provisioning exists and the sink is missing, the daemon will:
You can also override settings:
uv run stt-daemon run --device "alsa_input.usb-..." --language es --model base
uv run stt-daemon run -v # Verbose logging
uv run stt-daemon run --check # Test audio capture + model, then exit
list — Show devices and provisioning statusuv run stt-daemon list
Output shows:
.monitor source)clear — Remove virtual sink and loopbacksuv run stt-daemon clear
# or without confirmation prompt:
uv run stt-daemon clear --yes
This unloads all tracked modules and removes the state file. If
audio.json is already gone (reboot, manual
delete, crash), clear --yes still finds orphaned modules by name
(VirtualMicSink/VirtualMic via pactl list short modules) and unloads
them in reverse order (loopbacks → remap-source → null-sink), so the
command is idempotent — safe to re-run and to recover from
Sink 'VirtualMicSink' already exists but is not tracked.
up — Launch daemon, assistant, and optionally TUIuv run stt-daemon up # Start daemon + assistant (separate processes)
uv run stt-daemon up --with-tui # Also launch the interactive TUI viewer
This subcommand runs the entire system as a supervised set of child processes:
The supervisor manages their lifetimes: if any child exits or Ctrl+C is pressed, all
remaining children are cleanly shut down. Without --with-tui, you can still run
a separate uv run stt-tui in another terminal to view and control the assistant.
With --with-tui, the daemon and assistant output is discarded so it cannot corrupt
the screen, while the TUI keeps the terminal. Without --with-tui, all output goes to
the terminal. The supervisor writes nothing to disk.
Like all subcommands, up also accepts --config, --device, --backend,
--model, --language, and -v (verbose) flags.
Example workflow:
# One-command startup with TUI (everything stops when you quit the TUI)
uv run stt-daemon up --with-tui
# Or, start daemon + assistant only
uv run stt-daemon up
# Then in another terminal, launch the TUI whenever you want
uv run stt-tui
speaker-enroll — Pre-enroll a speaker from WAV filesuv run stt-daemon speaker-enroll --name Efren --wav audio/2026-09-28/143022-123-pause.wav
Registers a known speaker up front, without waiting for the daemon to mint a provisional id
from a live voice. --name is required; pass --wav once per clip, and each file
contributes one prototype (capped at max_prototypes, oldest evicted). On success the
command prints something like enrolled 'Efren' as spk_0002 (2 clip(s), 7.4s).
Each WAV must be 16 kHz mono 16-bit PCM; the clips the daemon saves under
<session_dir>/<id>/audio/ are already in that format, so they need no conversion, and each
must be at least [speaker].min_enroll_s seconds.
The store is single-writer and owned by the daemon, so stop it before enrolling, or pass
--force to bypass the refusal. Like every subcommand, speaker-enroll also accepts
--config.
--dedupe-threshold F (default 0.98) sets the cosine at or above which two prototypes of the
same speaker count as duplicates. The command strips near-duplicate prototypes, including any
pre-existing ones for the named speaker, before it finishes, and reports it as
removed N duplicate prototype(s) from the ....
Example with several clips:
uv run stt-daemon speaker-enroll --name Efren \
--wav audio/2026-09-28/143022-123-pause.wav \
--wav audio/2026-09-28/150210-456-end_of_turn.wav
speaker-enroll-seed — Assisted enrollment from a seed clipuv run stt-daemon speaker-enroll-seed --name "Efren Yevale Varela" --seed audio/efren-seed.wav
Enrolls from ONE clip you are certain is the speaker. The command scans --audio-dir (default
[output].session_dir, the session root transcripts/, searched recursively for *.wav), embeds
each *.wav, ranks them by cosine similarity to the seed, and offers
the ones at or above --min-score (default 0.55) and at least [speaker].min_enroll_s seconds
for review. --name and --seed are required; the seed must be 16 kHz mono 16-bit PCM.
Review is interactive: each candidate shows [i/N] <filename> (score X.XX, Y.Ys) and you choose
Accept, Skip, Play, or Quit. --count N (default 5) caps how many reviewed clips are accepted;
--yes skips the prompts and auto-accepts the top candidates (required when stdin is not a TTY);
--no-play disables playback; --max-scan N caps how many files are embedded. Quit or Ctrl+C
writes nothing and prints the equivalent stt-daemon speaker-enroll --wav ... command. Like
speaker-enroll, this refuses to run while a daemon appears to be running unless --force is
passed, and accepts --config.
Each candidate is flagged during review: duplicate when its embedding is already covered by one
of that speaker's prototypes (cosine at or above --dedupe-threshold, default 0.98), and
other-speaker:<name> when it scores highly against a different enrolled speaker (cosine at or
above --other-threshold, default 0.45), a contamination hint. Accepting a duplicate is
refused with skipping <name>: already covered by this profile, without consuming the accept
budget. The incoming set (seed plus accepted clips) is de-duplicated before enrolling and
pre-existing near-duplicate prototypes of that speaker are removed afterward, both reported on
stdout.
Example, accepting three reviewed clips non-interactively:
uv run stt-daemon speaker-enroll-seed --name "Efren Yevale Varela" \
--seed audio/efren-seed.wav --count 3 --yes
Tuning and rebuilding. Running either enrollment command for a name that already exists
appends prototypes (the oldest is evicted once max_prototypes is reached) rather than resetting
the profile, so you tune by enrolling more diverse clips and flush a bad prototype by adding enough
new ones. To start clean, stop the daemon, delete speakers/, and re-enroll. After any enrollment,
tune-up, or wipe, restart the daemon so it reloads the store. See the
Tuning an existing profile and
Wipe and rebuild sections of
docs/SPEAKERS.md.
speaker-eval — Measure accuracy against labeled clipsuv run stt-daemon speaker-eval --labeled labeled/
Scores the store against a directory of labeled clips. Each immediate subdirectory of --labeled
is a speaker display name and holds that person's *.wav clips (16 kHz mono 16-bit PCM). The
command embeds each clip, labels it with the same matcher the daemon uses, and prints a plain-text
report: total, accuracy, correct, wrong, uncertain (top-1 correct/total), unknown, a
per-speaker table (name | n | correct | acc), the top confusions (true -> predicted (count),
up to 10), and margins (count, mean, p10 of top1 - top2). The run is read-only, so it never
writes the store and the daemon does not need to be stopped. It requires the model and
uv sync --extra speaker, and fails clearly if the store was built with a different embedding
model (dimension mismatch).
--min-accuracy F (optional) exits non-zero when measured accuracy is below F, so it works as a
tuning gate.
Example, using it as a gate:
uv run stt-daemon speaker-eval --labeled labeled/ --min-accuracy 0.9
--list-sources flaguv run stt-daemon --list-sources
# Equivalent to: uv run stt-daemon list
--check flaguv run stt-daemon --check
# Equivalent to: uv run stt-daemon run --check
When you run setup or run without --sources, the daemon shows an interactive picker:
questionary to show a checkbox UI. Pre-checked items are from the previously saved selection.all, none, or blank (to keep previous selection).Device labels:
[IN] <description> (<name>) — Input device (real microphone)[OUT] <description> (<name>) — Output device (monitor of a sink)The simplest workflow:
# Create/setup the sink (interactive)
uv run stt-daemon setup
# Start the daemon in another terminal
# (auto-uses the provisioned sink)
uv run stt-daemon run
# Stop and remove the sink when done
uv run stt-daemon clear --yes