qqq 0.1.2

Quick Query Quorum: an in-memory multi-user voice chat with an AI facilitator
# 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.

The first interaction is a private AI conversation, not a group session. QQQ
creates a joinable session only after the user explicitly requests a group
conversation and provides a display name, language, and group purpose.

## Install

Install the latest release from crates.io:

```bash
cargo install qqq
```

Create the configuration and start QQQ:

```bash
cp .env.example .env
# Set OPENAI_API_KEY, CHAT_MODEL, STT_MODEL, TTS_MODEL, and TTS_VOICE.
qqq
```

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`:

```bash
docker run --rm -p 8787:8787 \
  --env-file .env \
  -e QQQ_BIND=0.0.0.0:8787 \
  ghcr.io/maxylev/qqq:latest
```

Pin a version tag such as `0.1.2` 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:

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo package --locked
docker build -t qqq:dev .
```

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:

```bash
set -a
source .env.test
set +a
cargo test live_ -- --ignored --test-threads=1
```

The paid suite validates private-to-group promotion, a three-participant
Brainstorm conversation, prompt-cache accounting, forced tool calls, and a real
TTS-to-STT round trip. Cache verification asserts a cold write followed by
cache reads and lower warm-request costs. Total cost is not expected to remain
constant because new input and output tokens are still billable. A manually
dispatched `Live provider E2E` GitHub workflow runs 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:

```nginx
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](LICENSE).