Chat SPBoucher AI
Interface privée, multi-modèles et multimodale de Simon-Pierre Boucher — https://chat.spboucher.ai — déployée sur le cluster MacLustr (nœud M4M64c, PostgreSQL 17 + pgvector, MacLustr Tunnel via BHS64).
Un seul propriétaire. Tous les modèles OpenAI et Anthropic vérifiés par requête réelle. Mémoire persistante. Contrôle complet.
Phase zéro — ce qui a été vérifié avant de coder
| OpenAI | Anthropic | |
|---|---|---|
| Modèles listés par l'API du compte | 140 | 14 |
| Modèles de chat vérifiés (11 tests réels chacun) | 30 | 14 |
| Listés mais indisponibles (404 « deprecated ») | 23 | 0 |
| Spécialisés vérifiés (image / TTS / STT / embeddings / modération) | 18 | — |
| Non exercés (realtime, vidéo, deep-research, completions) | 18 | — |
Livrables dans benchmarks/ : model-discovery.json, openai-results.json, anthropic-results.json, modality-results.json, capability-matrix.json, pricing.json (prix copiés des pages officielles le 2026-11-01), benchmark-report.md, routing-recommendations.md. Coût total du benchmark : ≈ 2,45 $. Relancer : pnpm bench:discover && pnpm bench && pnpm bench:modalities && pnpm bench:report && pnpm registry:refresh.
Le laboratoire de modèles (/models) affiche ces données ; le routeur (src/lib/ai/router.ts) ne choisit jamais un modèle non vérifié.
Architecture
Internet → BHS64 (Caddy TLS, WireGuard) → M4M64c:8500 Next.js 16 (App Router, React 19)
├─ src/proxy.ts garde de session (cookie httpOnly SameSite=Strict)
├─ src/lib/auth.ts Argon2id, sessions révocables, rate-limit, Origin check
├─ src/lib/ai/ adapters OpenAI (Responses API) & Anthropic (Messages, adaptive thinking,
│ fallback serveur), registre, routeur, coûts
├─ src/lib/chat/ moteur (streaming SSE, boucle d'outils, approbation, mémoire, titre, artefacts)
├─ src/lib/tools/ web_search (Tavily), fetch_page, python (sandbox-exec), search_documents (RAG),
│ generate_image (gpt-image), save_memory
├─ src/lib/research/ recherche approfondie (plan → sources → extraction → synthèse citée)
└─ PostgreSQL 17 + pgvector : conversations, messages (arbre), pièces jointes, chunks, mémoire,
artefacts, usage_events, comparisons, research_runs, tool_executionsDéveloppement
pnpm install
cp .env.example .env.local # puis remplir (clés API, DATABASE_URL local, SESSION_SECRET)
createdb chat_spboucher && psql chat_spboucher -c 'CREATE EXTENSION vector'
pnpm db:migrate # db/migrations/*.sql
pnpm registry:refresh # charge les modèles (listing live + benchmarks)
pnpm setup:owner # mot de passe du propriétaire (prompt masqué → hash Argon2id)
pnpm dev # http://localhost:8500
node scripts/e2e/smoke.mjs # test navigateur (Playwright) : login, chat, palette, mobileProduction (MacLustr)
Suivre docs/OPERATIONS.md : manifeste mld (deploy/mld-manifest.json → M1M32:~/dispatch/apps/chat-spboucher-ai.json), mld deploy chat-spboucher-ai --node M4M64c, route tunnel chat.spboucher.ai, sauvegardes chiffrées nocturnes (scripts/backup.sh, restauration scripts/restore.sh), rafraîchissement du catalogue à 05 h 17.
La source de vérité du code est le dépôt git sur le nœud (M4M64c:~/apps/chat-spboucher-ai, origin = spbgit chat-spboucher-ai.git) — règle remote-first MacLustr.
Sécurité
- Accès privé : aucune inscription, un seul propriétaire, Argon2id (64 Mio, t=3), 5 échecs / 15 min par IP puis blocage, sessions révocables,
Clear-Site-Dataau logout. - Secrets uniquement dans
.env.production(mode 600) et le manifestemld; jamais dans git. La clé Anthropic collée dans le chat de conception renvoyait 401 : la clé du compte déjà utilisée par les autres apps a été employée (voir~/Desktop/Cluster/secrets/chat-spboucher-ai.env). - Outils : Python exécuté sous
sandbox-exec(pas de réseau, répertoire jetable, 20 s) ;fetch_pagerefuse les adresses privées ; les artefacts HTML/React s'affichent dans un iframesandbox="allow-scripts"sans origine partagée. - Mémoire : refus automatique des contenus ressemblant à des identifiants ; approbation optionnelle ; export/suppression.
- Limites de dépenses locales (jour / mois) qui bloquent les nouvelles requêtes ; la facturation réelle des fournisseurs peut différer.