OpenVoiceOS STT HTTP Server¶
In a nutshell
This is a small standalone program that turns any OVOS speech-to-text engine into a web service. Speech-to-text is the part that converts spoken audio into written text. Once it's running, other devices on your network (or the internet) can send it audio over a simple web request and get back the transcribed text. This lets one capable machine do the listening for many lightweight devices. It can even pretend to be popular cloud services (like OpenAI Whisper or Google), so software written for those works against your own server unchanged. See STT plugins and the Glossary.
Lightweight HTTP microservice for any OVOS speech‑to‑text plugin.
The OpenVoiceOS STT HTTP Server wraps your chosen OVOS STT plugin inside a FastAPI service, complete with automatic language detection. This makes it easy to deploy on your local machine, in Docker, or behind a load balancer.
Usage Guide¶
Install the server
Choose your STT plugin
You name the plugin to serve with the --engine flag (below). When no explicit config is
passed, the server reads the plugin's own section from mycroft.conf (stt.<plugin-id>),
matching OVOSSTTFactory behavior, so a mounted config file can select the model, device,
and other plugin settings. With no config file present, the plugin runs with its built-in
defaults.
A normal OVOS install (not this server) selects its STT plugin under the stt section instead:
Launch the server
Verify it's running
Visit http://localhost:8080/status in your browser or run:
Command‑Line Options¶
$ ovos-stt-server --help
usage: ovos-stt-server [-h] --engine ENGINE [--lang-engine LANG_ENGINE] [--port PORT] [--host HOST] [--multi] [--mcp]
options:
-h, --help show this help message and exit
--engine ENGINE stt plugin to be used
--lang-engine LANG_ENGINE
audio language detection plugin to be used
--port PORT port number
--host HOST host
--multi Load a plugin instance per language (force lang support)
--mcp mount MCP server at /mcp (requires ovos-stt-http-server[mcp])
The short help text leaves out the defaults: the port defaults to 8080 and the host to
0.0.0.0, and --engine is the one required flag.
Technical Explanation¶
-
FastAPI core
The server is a FastAPI app served byuvicorn, exposing REST endpoints. -
Plugin wrapping
--enginenames anyopm.sttplugin entry point (Whisper, Deepgram, and so on). It is loaded dynamically via the OVOS Plugin Manager. When no explicit config is passed, the server reads the plugin's own section frommycroft.conf(stt.<plugin-id>), matchingOVOSSTTFactorybehavior, so a mounted config file can select model, device, and other plugin settings. -
Language detection
--lang-enginenames anopm.transformer.audioplugin implementingAudioLanguageDetector. When a/sttrequest passeslang=auto, audio is routed through it before transcription. -
Multi-model mode
--multiloads one engine instance per language on demand (one model perlang), instead of a single shared model. -
Compatibility routers
Beyond the native endpoints, the app mounts drop-in compatible routers so existing cloud-STT clients work unchanged. Examples are Wit.ai (POST /wit/speech, override the SDK host with theWIT_URLenv var) and Chromium speech-api (POST /speech-api/v2/recognize). The app also routes for OpenAI Whisper, Whisper.cpp server, Deepgram, Google, AssemblyAI, Azure, IBM Watson, AWS Transcribe, Vosk, Speechmatics, Gladia, ElevenLabs Scribe, Groq, and Kaldi GStreamer. See/docsfor the authoritative set. AGET /utcpmanual advertises the endpoints to UTCP agents. An MCP endpoint mounts at/mcpon the same port when the server is started with--mcp(requirespip install --pre 'ovos-stt-http-server[mcp]>=0.26.0a1'). The extra alone no longer auto-mounts it.The
mcpextra installsfastmcp, not themcpSDKThe extra keeps the name
mcp, but it resolves the third-partyfastmcp>=3,<4package, not the officialmcpSDK (MCP SDK 2.0 removedmcp.server.fastmcp.FastMCP, so a server still importing that symbol fails on the 2.x SDK). This server serves MCP withfastmcp; a client consuming a different MCP server (likeovos-mcp-toolbox, see Agent Tool Plugins) uses the officialmcpSDK instead. -
Scalability
Stateless design lets you run multiple instances behind a load balancer or in Kubernetes.
HTTP API Endpoints¶
Native endpoints:
| Endpoint | Method | Description |
|---|---|---|
/status |
GET | Returns {status, plugin, lang_plugin}. |
/stt |
POST | Raw audio bytes in the body (query: lang, sample_rate default 16000, sample_width default 2) → plain‑text transcript. With lang=auto, language is detected first. |
/lang_detect |
POST | Raw audio bytes (query: valid_langs) → JSON { "lang": "en", "conf": 0.83 }. |
/utcp |
GET | UTCP tool-discovery manual (JSON). |
/docs |
GET | Interactive FastAPI OpenAPI docs. |
Compatibility routers (selection): POST /wit/speech (Wit.ai), POST /speech-api/v2/recognize (Chromium), plus OpenAI/Deepgram/Google/etc. See /docs for the full list.
OpenAI-Compatible Translation Endpoint¶
POST /openai/v1/audio/translations mirrors OpenAI's Whisper translations endpoint. OpenAI's
contract for this endpoint always returns English, regardless of the spoken language. So
after transcribing, the request runs an extra translate step using the configured OVOS
translate plugin. If no translate plugin is configured, the endpoint falls back to returning
the untranslated transcript instead of failing.
Transformer Pipelines¶
The server can run OVOS transformer plugins around transcription. Both hooks live in the
model containers, so every surface gets them: the native /stt endpoint, all
vendor-compat routers, the websocket streaming routes, MCP, and even UTCP.
- Audio transformers run before the STT stage. When the chain includes an
AudioLanguageDetector(like the--lang-enginementioned above), its detected language resolveslang=auto(an explicitly requested language always wins). - Utterance transformers rewrite the transcript after ASR, before it is returned.
Loading is config-gated and opt-in via the standard mycroft.conf sections. With no config
the server behaves exactly as before:
{
"audio_transformers": {
"ovos-audio-transformer-plugin-speechbrain-langdetect": {}
},
"utterance_transformers": {
"ovos-utterance-corrections-plugin": {}
}
}
Chains run in ascending priority order. An explicit "order" list in a section wins over
priorities. In multi-model mode (--multi) one set of transformer instances is shared
across the per-language engines. See the
transformer plugins reference for the full contract.
Server-side transforms are a client-invisible change
An utterance transformer on the server means clients receive a different transcript
than what the STT engine actually heard. Use it deliberately: for fleet-wide vocabulary
corrections, language routing via an AudioLanguageDetector, or server-side audio
cleanup for thin clients. Never enable the same plugin on both the server and a
downstream OVOS stack that also runs transformers, or it will run twice.
Companion Plugin¶
To point a OpenVoiceOS (or compatible project) to a STT server you can use the companion plugin.
Package name vs. repository name
The PyPI package is ovos-stt-plugin-server, but its source repository is named
OpenVoiceOS/ovos-stt-server-plugin (the words are swapped). Both names refer to the same
plugin. Use the pip name to install, the repo name to find the source.
Install
Upcoming — universal server adapter
A server_type option is planned for this companion plugin, so a single config shape can
target different self-hosted or cloud STT server APIs without a dedicated plugin per vendor.
Key name: urls, not host
This STT companion plugin reads the urls key (a list of strings). The
TTS companion plugin reads a different key,
host. The two are not interchangeable. If you set the wrong key, the
plugin does not error. It silently ignores the value and falls back to the
public servers described below.
Configure
Point it at your own server (localhost, or wherever you run the container above):
"stt": {
"module": "ovos-stt-plugin-server",
"ovos-stt-plugin-server": {
"urls": ["http://localhost:8080/stt"],
"verify_ssl": true,
"user_agent": "my-ovos-client",
"timeout": 5
},
}
Restart and verify
After editing the config, restart the client so it picks up the change, then confirm it is actually talking to your server:
Say a command and check the voice/audio logs, or watch live traffic with
ovos-busmon, to confirm the configured urls server is the one
receiving the request, not a public fallback.
for audio language detection
"audio_transformers": {
"ovos-audio-lang-server-plugin": {
"urls": ["http://localhost:8080/lang_detect"],
"verify_ssl": true
}
}
audio_transformers goes at the top level, not under listener
The plugin is constructed with no config, so it falls back to
AudioTransformer._read_mycroft_conf(), which reads the top-level audio_transformers
block only. A listener.audio_transformers block is read by the transformer service,
which logs it as deprecated, but never reaches this plugin.
Put the block in the wrong place and urls is silently empty. The plugin then falls back to
its built-in default and posts your audio to a public server on the internet. There is no
error to notice.
The singular url key (a single string or a list) is also accepted as an alias for urls,
and takes precedence over urls when both are set. The optional user_agent (STT client and
lang-detect classifier alike) overrides the request User-Agent sent to servers. It defaults
to the standard OVOS client UA.
No urls configured → public servers, not local failure
If you omit urls entirely, the plugin does not fail. It silently falls back to a
small built-in list of public OVOS STT servers (shuffled, tried in order) run by
community members. That's convenient for a quick test, but it means your audio leaves your
network by default unless you set urls yourself. Always set urls explicitly for any
real deployment.
Community servers are best-effort demos
The public OVOS servers exist for easy onboarding and demos only. They are best-effort, not optimized, carry no uptime guarantees, and may vanish at any time. OVOS will be slow and unreliable if you rely on them. The official recommendation is to self-host — or skip servers entirely: fully offline plugins exist for everything.
See STT plugins for fully offline engines if you'd rather not depend on any server.
urls semantics:
- List, tried in order until one succeeds: if you list more than one server, the plugin
tries each in turn (each attempt bounded by
timeout, default 5 seconds) and returns the first successful transcription. It does not race them in parallel. timeoutis per-attempt, in seconds, not a total budget across the whole list.- A request that exhausts every URL without success raises no exception from the plugin call itself. The caller (the listener, below) just gets no transcript back.
Listener-side fallback: a second STT engine, separate from the URL retry¶
Independent of this plugin's own multi-URL retry, ovos-dinkum-listener supports a completely
separate fallback STT engine. This is a different plugin entirely, used only if the primary
engine returns no utterance. Configure it under the stt section:
{
"stt": {
"module": "ovos-stt-plugin-fasterwhisper", // primary, offline
"fallback_module": "ovos-stt-plugin-server", // used only if the primary returns nothing
"ovos-stt-plugin-server": {
"urls": ["http://localhost:8080/stt"]
}
}
}
This is also useful the other way around: a fast, light primary engine locally, with a heavier server-backed engine as the fallback for when the light one comes back empty.
Docker Deployment¶
Create a Dockerfile
FROM python:3.11-slim
RUN pip install --pre "ovos-stt-http-server>=0.25.1a3"
RUN pip install {YOUR_STT_PLUGIN}
ENTRYPOINT ["ovos-stt-server", "--engine", "{YOUR_STT_PLUGIN}"]
The console script is ovos-stt-server (not ovos-stt-http-server, which is the PyPI package name).
Build & Run
Pre-built containers are also available via the ovos-docker-stt repository.
Tips & Caveats¶
-
/stttakes raw audio bytes, not a multipart upload. Send the PCM/WAV bytes as the request body (curl --data-binary @audio.wav). Passsample_rate/sample_widthas query params if they differ from the 16000/2 defaults. Those defaults are assumed when reading the raw body. -
Audio Formats: the native
/sttendpoint takes raw PCM/WAV bytes. The vendor-compatible multipart routers (OpenAI, ElevenLabs, Groq, and the rest) accept other container formats only whenpydubis installed, which the base install omits:pip install --pre 'ovos-stt-http-server[audio]>=0.25.1a3'. Without that extra, uploading anything other than WAV through those routers returns HTTP 501 naming the missing format support. -
Securing Endpoints: Consider putting a reverse proxy (NGINX, Traefik) in front for SSL or API keys. Minimal NGINX server block, proxying plain HTTP to the server on port 8080:
server {
listen 80;
server_name stt.example.lan;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
Add TLS yourself, for example with certbot --nginx (see the
Certbot documentation). See also
tts-server: reverse proxy for the TTS side.
- Plugin Dependencies: Some STT engines require heavy native libraries. Bake them into your Docker image.
Links & References¶
-
OVOS STT HTTP Server GitHub: OpenVoiceOS/ovos-stt-http-server
-
Companion Plugin: OpenVoiceOS/ovos-stt-server-plugin
-
Docker Images: OpenVoiceOS/ovos-docker-stt
-
OVOS Plugin Manager: OpenVoiceOS/ovos-plugin-manager
Read next: TTS Server Related: Translate Server · Server Compatibility Layers · STT Plugins · Privacy & Security