Docker processor
Last modified: 25 July 2026
What it is
The processor is a container that turns utterances into verdicts. The plugin posts to it; it transcribes, matches the rule packs, and answers. Self-hosting it means audio never leaves your infrastructure.
| Endpoint | Purpose |
|---|---|
POST /v1/moderate | What the plugin calls. Bearer token per server. |
GET /healthz | Liveness. Point your monitoring here. |
GET /v1/usage | Per-server request counters. Bearer auth. |
The reference deployment runs the model parakeet-tdt-0.6b-v3-int8 (English and
Czech), downloaded on first start into the container's model directory, with the rule packs mounted
read-only.
Point the plugin at it
engine:
mode: sidecar
processor:
url: http://127.0.0.1:8090/v1/moderate
token: 'the token for this server'
Then restart the server and check /vs status. Everything else keeps its
defaults.
Tokens and entitlements
The processor's tokens.json is the enforcement surface. One entry per server:
{"server-id": {"token": "…", "plan": "starter", "languages": ["en"],
"rate-limit-per-minute": 120, "expires": "2027-01-01T00:00:00+00:00"}}
Entries are minted from the licence, so a customer cannot widen their own access by editing a config. Every request is checked, and refusals are explicit:
| Response | Meaning |
|---|---|
401 unauthorized | Missing or wrong token. |
403 language_not_licensed | A pinned language outside the plan. Auto-detect narrows to licensed packs instead of failing. |
403 license_expired | The entry's expiry has passed. |
429 rate_limited | Over the per-minute allowance. Carries Retry-After. |
503 processor_busy | Admission full. The plugin fails open — see when moderation is down. |
Two things that crash-loop it
- File ownership.
tokens.jsonmust be readable by the container user (uid 10001). A root-owned600file makes the service exit complaining that the file "must contain a readable JSON object" — the message wraps a permission error and sends people hunting for a JSON typo that is not there. The model directory needs the same treatment or the first-start download cannot write. - Idle connection closes. The plugin pools HTTP connections. If anything in front of the processor closes idle connections aggressively, the next request lands on a dead socket and shows up as an intermittent timeout. Keep the processor's keep-alive window generous; the plugin deliberately drops its own idle connections earlier than the server does.
Operating it
docker logs -f voicesniffer-processor # per-request stage traces
docker compose restart # bounce it
docker compose up -d --build # after updating the source
The logs carry a request id per utterance and a stage trace. When a timeout shows up in the server log, grep that id here — it tells you which stage ran long instead of leaving you guessing between network, queue and inference.