QQQ
Quick Query Quorum is a minimal, in-memory multi-user voice chat with an OpenAI-compatible AI facilitator. It provides one self-contained Rust binary and a browser interface with no database or frontend build step.
Sessions, credentials, history, and generated audio exist only in process
memory and disappear on restart. Participant tokens remain in browser
sessionStorage; they are sent as bearer credentials and never appear in URLs
or session views.
Install
Install the latest release from crates.io:
Create the configuration and start QQQ:
# Set OPENAI_API_KEY, CHAT_MODEL, STT_MODEL, TTS_MODEL, and TTS_VOICE.
Open http://127.0.0.1:8787 and press the orb.
To run directly from a checkout, use cargo run --release. Exported variables
override values loaded from .env.
Docker
Release images are published for linux/amd64 and linux/arm64:
Pin a version tag such as 0.1.1 in production. QQQ must bind to 0.0.0.0
inside a container for the published port to be reachable.
Configuration
| Variable | Default | Purpose |
|---|---|---|
QQQ_BIND |
127.0.0.1:8787 |
Listener address |
QQQ_PUBLIC_URL |
unset | Optional externally visible URL |
OPENAI_BASE_URL |
https://openrouter.ai/api/v1 |
OpenAI-compatible API base URL |
OPENAI_API_KEY |
required | Server-only provider credential |
CHAT_MODEL |
required | Chat-completion model |
STT_MODEL |
required | Speech-to-text model |
TTS_MODEL |
required | Text-to-speech model |
TTS_VOICE |
required | Text-to-speech voice |
TTS_FORMAT |
mp3 |
Speech response format |
OPENAI_HTTP_REFERER |
unset | Optional provider attribution header |
OPENAI_APP_TITLE |
unset | Optional provider attribution header |
MAX_SESSIONS |
100 |
Maximum active in-memory sessions |
MAX_PARTICIPANTS_PER_SESSION |
8 |
Maximum participants per session |
SESSION_TTL_SECONDS |
14400 |
Idle session lifetime |
MAX_HISTORY_MESSAGES |
512 |
Append-only history limit |
MAX_AUDIO_BYTES |
10000000 |
Multipart audio upload limit |
INPUT_BATCH_MS |
350 |
Model input batching interval |
DELIVERY_TIMEOUT_SECONDS |
45 |
Delivery progress timeout |
CLAIM_MODE |
replace |
Floor claim behavior: replace or queue |
DEFAULT_LANGUAGE |
en |
Onboarding and STT fallback language |
The selected provider and base URL must support chat completions, speech generation, and transcription.
API Model
Public routes are GET /, GET /health, and
GET /api/sessions/:id/exists. Session creation and joining use
POST /api/sessions and POST /api/sessions/:id/join. All other session
routes require bearer authentication, including the WebSocket hello at
/api/sessions/:id/ws.
History begins with one stable system message and is append-only. User messages
preserve arrival order, and each assistant send_messages tool call remains
paired with its tool result. Each session runs one model call at a time.
Deliveries run sequentially, and only the current floor holder may record.
Development
Run the same checks as CI:
Offline tests exercise in-process Axum routes, authorization, multipart and raw audio validation, delivery transitions, and WebSocket semantics.
Live Provider Tests
Ignored live tests use real provider requests and spend credits. Put credentials
and model names in the ignored .env.test, then source it without printing its
contents:
The first test validates a forced chat tool call. The second performs a real
TTS-to-STT round trip. A manually dispatched Live provider E2E GitHub workflow
can run the same checks with secrets from the protected openrouter-e2e
environment.
HTTPS And Limits
Mobile microphone access requires trusted HTTPS. Terminate TLS at a same-origin reverse proxy; QQQ intentionally does not implement TLS:
location / {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
QQQ intentionally provides one process with no persistence, accounts, recovery, audio broadcast, database, or history truncation. A session closes when it reaches its history limit.
License
QQQ is available under the MIT License.