~/.config/horchd/config.toml is the source of truth. Hand-editable;
horchctl reload re-reads it without dropping the audio thread.
horchctl wakeword {threshold,enable,disable,cooldown,add,remove} --save
mutate the file in place while preserving comments and formatting.
Full example
[engine]
device = "default" # cpal device name; "default" = system default mic
sample_rate = 16000 # must be 16000; horchd refuses other rates
log_level = "info" # trace | debug | info | warn | error
[engine.shared_models]
melspectrogram = "/usr/local/share/horchd/melspectrogram.onnx"
embedding = "/usr/local/share/horchd/embedding_model.onnx"
[[wakeword]]
name = "alexa" # unique id; appears in D-Bus signal
model = "~/.local/share/horchd/models/alexa_v0.1.onnx"
threshold = 0.5 # score must reach this to fire
cooldown_ms = 1500 # don't refire within this window
enabled = true # toggle without unloading
[[wakeword]]
name = "hey_jarvis"
model = "~/.local/share/horchd/models/hey_jarvis_v0.1.onnx"
threshold = 0.65
cooldown_ms = 1500
[wyoming] # off by default — see "Wyoming server"
enabled = false
mode = "wyoming-server" # local-mic | wyoming-server | hybrid
listen = ["tcp://0.0.0.0:10400"]
zeroconf = true # advertise _wyoming._tcp.local. for HA auto-discovery
# service_name = "horchd-living" # default = "horchd-<hostname>"
Field reference
[engine]
| Field | Type | Default | Notes |
|---|---|---|---|
device |
string | "default" |
cpal device name. "default" = system default mic. |
sample_rate |
int | 16000 |
Must be 16000. Documented sanity check. |
log_level |
string | "info" |
RUST_LOG env wins if set. |
[engine.shared_models]
| Field | Type | Required | Notes |
|---|---|---|---|
melspectrogram |
path | yes | Universal openWakeWord melspec model. |
embedding |
path | yes | Universal openWakeWord embedding model. |
Both ship with the upstream Python openwakeword package and are not
per-wakeword. install.sh puts them under /usr/local/share/horchd/.
[[wakeword]]
| Field | Type | Default | Notes |
|---|---|---|---|
name |
string | — | Required. Unique. Appears in the D-Bus Detected signal. |
model |
path | — | Required. .onnx classifier. ~ expansion supported. |
threshold |
float | 0.5 |
Score must rise to this for a fire. |
cooldown_ms |
int | 1500 |
Suppress refire within this window. |
enabled |
bool | true |
Toggle without unloading. |
[wyoming]
Embedded Wyoming-protocol server. See the
Home Assistant recipe for the
end-to-end walkthrough. Off by default; toggle at runtime with
horchctl wyoming enable --save.
| Field | Type | Default | Notes |
|---|---|---|---|
enabled |
bool | false |
Whether to bind the listener at boot. |
mode |
string | "local-mic" |
local-mic (server-owned mic) · wyoming-server (client streams audio — HA standard) · hybrid (both). |
listen |
string[] | ["tcp://0.0.0.0:10400"] |
URI scheme tcp:// · unix:// · stdio://. Multiple OK. |
zeroconf |
bool | true |
Advertise _wyoming._tcp.local. for HA auto-discovery. |
service_name |
string? | horchd-<hostname-suffix> |
mDNS service label. |
Multiple aliases on the same model
You can have multiple [[wakeword]] entries pointing at the same model = "..." path with different name, threshold, and cooldown_ms values.
Each unique model file is loaded into the ONNX runtime exactly once but the
detector state machine runs per [[wakeword]] entry — so N detectors
against M unique models cost M classifier evaluations per frame, not N.