Getting Started
Run the whole Clippy Chat app locally. You need Node 22+, Docker (for the dev Postgres), and access to a vLLM OpenAI-compatible endpoint for live replies.
1. Clone and install
git clone https://git.cdot.io/cdot.io/clippy-chat.git
cd clippy-chat
npm install
2. Start the dev database
A Postgres 17 container on localhost:5433 (see docker-compose.yml):
docker compose up -d db
3. Configure the environment
Copy the template and fill it in. Every value in .env.example is a placeholder:
cp .env.example .env
The minimum to boot:
DATABASE_URL=postgres://clippy:clippy@localhost:5433/clippy
APP_URL=http://localhost:3000
INFERENCE_BASE_URL=http://<vllm-host>:8000
INFERENCE_MODEL=@vllm2/unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL
# INFERENCE_AUTH_MODE=gateway # against AIRS; omit for a direct vLLM
# INFERENCE_API_KEY=... # direct mode only if vLLM runs with --api-key
# Gateway mode credential: a Keycloak client_credentials identity scoped
# completions.write. Falls back to INFERENCE_API_KEY when unset.
# INFERENCE_TOKEN_URL=https://auth.example.com/realms/myrealm/protocol/openid-connect/token
# INFERENCE_CLIENT_ID=clippy-inference-client
# INFERENCE_CLIENT_SECRET=...
KC_ISSUER=https://auth.example.com/realms/myrealm
KC_CLIENT_ID=clippy-web
KC_CLIENT_SECRET=changeme
SESSION_SECRET=<openssl rand -base64 32>
ADMIN_USERNAME=admin
ADMIN_PASSWORD=changeme
M2M_SCOPE=clippy-api
# Expected aud on machine bearer tokens. Verify a real token carries this value
# before setting it — arming it against a missing aud 401s every machine caller.
# M2M_AUDIENCE=clippy-api
See Configuration for every variable, its purpose, and defaults.
:::tip No Keycloak handy?
You don't need a live OIDC provider to try the app. The local break-glass admin
(ADMIN_USERNAME / ADMIN_PASSWORD) logs in without Keycloak — see
Authentication.
:::
4. Apply migrations
npm run db:migrate
5. Run the dev server
npm run dev
Open http://localhost:3000. Unauthenticated requests redirect to /login; sign in with the
local admin account, start a new chat, and send a message — the reply streams in token by token.
Test, typecheck, build
npm test # vitest; integration tests need the dev db (docker compose up -d db)
npm run typecheck # tsc --noEmit
npm run build && npm start
Next steps
- Red-Team Adapter — the reason this app exists.
- Architecture — how the pieces fit together.
- HTTP API Reference — every endpoint.