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.

EndpointPurpose
POST /v1/moderateWhat the plugin calls. Bearer token per server.
GET /healthzLiveness. Point your monitoring here.
GET /v1/usagePer-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:

ResponseMeaning
401 unauthorizedMissing or wrong token.
403 language_not_licensedA pinned language outside the plan. Auto-detect narrows to licensed packs instead of failing.
403 license_expiredThe entry's expiry has passed.
429 rate_limitedOver the per-minute allowance. Carries Retry-After.
503 processor_busyAdmission full. The plugin fails open — see when moderation is down.

Two things that crash-loop it

  • File ownership. tokens.json must be readable by the container user (uid 10001). A root-owned 600 file 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.

Copied