spb/chat-spboucher Public
Private universal chat interface over the OpenRouter ecosystem — 400+ models, branching, streaming, usage tracking. Next.js 16 + SQLite, PWA, deployed on m4m64a at chat.spboucher.ai
TypeScript 78.8%
CSS 15.1%
JavaScript 4.9%
Shell 1.2%
1# chat.spboucher.ai2345678910111213> **Author:** Simon-Pierre Boucher14> **Contact:** [contact@spboucher.ai](mailto:contact@spboucher.ai)15> **Production:** [https://chat.spboucher.ai](https://chat.spboucher.ai)1617A private, universal chat interface over the entire **OpenRouter** ecosystem — one login, one server-side API key, 400+ models behind a single catalog, full conversation persistence with branching and per-message model attribution. Designed mobile-first as an installable PWA: a personal ChatGPT/Claude-class product, not an API demo.1819---2021## Highlights2223- **One gateway, hundreds of models** — the catalog is synced dynamically from OpenRouter into SQLite (never hand-maintained); removed models are marked `Unavailable`, historical conversations stay intact forever.24- **Real streaming** — normalized SSE event protocol (`generation.start`, `content.delta`, `reasoning.delta`, `usage`, …) with 15 s keep-alive pings that survive ngrok and mobile radio sleep. Stop truly aborts the upstream OpenRouter stream.25- **Branching & regeneration** — regenerating never destroys an answer; siblings live side by side with ‹ 1/2 › navigation, so the same prompt can be compared across models.26- **Generation state machine** — `queued → starting → streaming → completed | cancelled | failed`, persisted per generation; a dropped mobile connection resyncs from the server.27- **Usage & cost tracking** — prompt/completion/reasoning/cached tokens and reported cost per generation, with a Today / 7d / 30d / All-time dashboard broken down by model.28- **Instrument Panel design** — ink/graphite surfaces, one teal accent for "alive", one amber accent for "cost", mono type for model IDs and meters, and the signature **Model Rail** cartridge attached to the composer (provider glyph · mono model ID · live context meter · price-per-1M chip).29- **Security posture for a public tunnel** — argon2id single-user auth, DB-backed sessions, aggressive login rate limiting, `Secure`/`HttpOnly`/`SameSite` cookies, CSP + security headers, zero unauthenticated API surface.3031## Architecture3233```34Browser (phone or desktop, PWA)35 │ POST /api/chat (SSE)36 ▼37Next.js 16 backend — node m4m64a (Apple Silicon, macOS)38 │ src/lib/openrouter/ (single client module, normalized errors/streaming)39 ▼40OpenRouter API ──► Anthropic · OpenAI · Google · Meta · Qwen · DeepSeek · Mistral · …41```4243- **Runtime:** single Node.js process (API + UI), port 3000, exposed via ngrok at `chat.spboucher.ai`44- **Database:** SQLite (better-sqlite3, WAL) — conversations, message tree, generations, usage, model catalog cache, sessions45- **Supervision:** three launchd agents (`app`, `ngrok`, nightly `backup` at 03:30 with 14-day retention)4647## Local development4849```bash50cp .env.example .env # fill in OPENROUTER_API_KEY51npm install52npm run set-password <username> [password] # generates a strong one if omitted53npm run dev54```5556## Deployment (m4m64a)5758```bash59rsync -az --delete --exclude node_modules --exclude .next --exclude data --exclude .env \60 ~/Desktop/chat-spboucher/ M4M64a:apps/chat.spboucher.ai/61ssh M4M64a '~/apps/chat.spboucher.ai/ops/deploy.sh'62```6364| launchd agent | Role |65|---|---|66| `ai.spboucher.chat.app` | `npm run start` on port 3000, KeepAlive |67| `ai.spboucher.chat.ngrok` | `ngrok http --url=chat.spboucher.ai 3000`, KeepAlive |68| `ai.spboucher.chat.backup` | nightly SQLite backup 03:30, 14-day retention |6970## API surface7172All routes require a session cookie except `POST /api/auth/login`.7374| Endpoint | Purpose |75|---|---|76| `POST /api/chat` | send a message or regenerate (`{regenerateOf}`) — returns a normalized SSE stream |77| `POST /api/generations/:id/cancel` | abort the upstream OpenRouter generation |78| `GET /api/models` · `POST /api/models/sync` | cached catalog · manual refresh |79| `POST /api/models/prefs` | favorites / pinned models |80| `GET/PATCH/DELETE /api/conversations[/:id]` | conversation CRUD, branch leaf selection |81| `GET /api/usage?period=today\|7d\|30d\|all` | usage & cost dashboard data |8283## License8485Private software — © Simon-Pierre Boucher. All rights reserved.8687---8889**Simon-Pierre Boucher** · [contact@spboucher.ai](mailto:contact@spboucher.ai) · [chat.spboucher.ai](https://chat.spboucher.ai)90