Déploiement
Simmetric Chat est air-gap capable et local-first. Cinq cibles de déploiement : Docker Compose multi-container (recommandée pour la production), container unique all-in-one (environnements air-gapped), stack géré Coolify, override dev (hot-reload sur Doc…
Simmetric Chat est air-gap capable et local-first. Cinq cibles de déploiement : Docker Compose multi-container (recommandée pour la production), container unique all-in-one (environnements air-gapped), stack géré Coolify, override dev (hot-reload sur Docker) et desktop Tauri. Tous les artefacts vivent dans docker/ ; le shell desktop dans src-tauri/. Les images publiées sur GHCR sont uniquement amd64 (linux/amd64 - les builds arm64 émulées via QEMU se bloquaient ; sur arm64 on fait un self-build local depuis l'hôte natif). Tous les containers utilisent un healthcheck wget (--spider) : server GET /api/health, collector GET /api/health sur 3210, widget GET /health sur 3211, Postgres pg_isready, Redis redis-cli ping.
Prérequis
- Node.js ≥ 24, pnpm 11.24.0, PostgreSQL 16.
- Pour l'embedding local : ~2 GB RAM par
all-MiniLM-L6-v2(Xenova, CPU). - Pour le LLM local : Ollama avec modèle(s) pré-téléchargés (
gemma4:latestpar défaut). - Pour l'OCR vision : Ollama avec modèle vision (
glm-ocr:latestpar défaut).
Docker Compose (production)
Le compose de production (docker/docker-compose.yml) définit 6 services actifs : frontend (nginx, :80/:443), server (:3000), collector (:3210), widget (:3211), postgres (16, image pgvector/pgvector:pg16), redis (7-alpine, cache de scaling optionnel). ollama et qdrant sont désactivés par défaut (blocs commentés : décommente + définis OLLAMA_BASE_URL / VECTOR_DB_PROVIDER=qdrant pour les utiliser).
cp .env.example .env
# Modifie .env : JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, DATABASE_URL, LICENSE_KEY (si Enterprise)
docker compose -f docker/docker-compose.yml up -dEnv critiques de production (voir 03 - Configuration) :
JWT_SECRET- obligatoire, matériel cryptographique (dériveENCRYPTION_KEYsi unset).COLLECTOR_SECRET- obligatoire, doit matcher entre server et collector.WIDGET_API_KEY- obligatoire pour le widget, doit matcher entre widget et server.DATABASE_URL- PostgreSQL 16.LICENSE_KEY+LICENSE_SECRET- uniquement Enterprise.ENCRYPTION_KEY(base64 32 byte) - recommandé pour la persistance data-at-rest au-delà des rotations deJWT_SECRET.
Rotation des clés : définis LEGACY_PREVIOUS_ENCRYPTION_KEYS (CSV base64 32-byte)pour maintenir la decryptabilité des backups/données anciennes pendant la rotation. Il n'existe pas
de job automatique de re-encryption : procédure manuelle decrypt-old → re-encrypt-new.
Auto-provisioning des clés (entrypoint server)
L'entrypoint de production (docker/entrypoint-server.sh) exécute à chaque boot : le provisioning de ENCRYPTION_KEY et API_KEY_HMAC_SECRET avec la précédence valeur operator-supplied > valeur persistée dans le volume server-storage > généré une fois et persisté (jamais régénéré sur du matériel existant) ; puis prisma migrate deploy + seed et le démarrage du serveur. Les configurations dev (docker-compose.dev.yml) sautent le provisioning et gardent le fallback scrypt.
Volumes & persistance
- PostgreSQL : volume dédié (
pgdata). - LanceDB :
${STORAGE_PATH}/vectors/lancedb(collector). - File storage :
${STORAGE_PATH}(collector, documents/avatar/uploads). - Backup : destinations configurées par-job (disk/S3/Dropbox/...).
Health check post-deploy
Les containers s'auto-vérifient via wget (voir intro). Checks manuels équivalents :
curl http://localhost:3000/api/health
# { "status": "ok", "checks": { "database": "ok", "collector": "ok", "disk": "ok" } }
curl http://localhost:3210/api/health # collector
curl http://localhost:3211/health # widgetContainer unique all-in-one
docker/Dockerfile construit une image unique supervisée par supervisord qui exécute PostgreSQL 16 (pgvector compilé offline pour l'air-gap), server, collector et widget. Il expose 3000/3210/3211, utilise DATABASE_URL sur localhost, healthcheck sur /api/health. Pensée pour les environnements air-gapped/offline/demo : les env par défaut sont des placeholder - surcharge-les via .env ou -e.
Coolify
docker/docker-compose.coolify.yml + docker/nginx.coolify.conf automatisent le build des images, le networking interne, les volumes, les healthchecks et le mount du plugin enterprise (chemin hôte absolu ${ENTERPRISE_PLUGIN_PATH:-/opt/simmetric-enterprise}). Les secrets requis utilisent la syntaxe :? (Coolify refuse le deploy s'ils manquent) : JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, POSTGRES_PASSWORD, APP_URL, ALLOWED_ORIGINS. Différences avec le compose de base : pas de container_name, aucune porte hôte sur les apps (ingress confié à Traefik, HTTP-only - TLS terminé par Traefik), ENCRYPTION_KEY/API_KEY_HMAC_SECRET non interpolées (auto-provisionnées dans le volume).
Développement local
pnpm install
pnpm db:generate # Prisma client (+ fix symlink Prisma/pnpm)
pnpm --filter server db:migrate # applique les migrations
pnpm dev # turborepo: server + collector + widget + frontend en parallèleDétails dans 10 - Development.
Override dev (Docker)
docker/docker-compose.dev.yml monte les sources sur le compose de production pour le hot-reload (NODE_ENV=development). docker-compose.infra.yml démarre uniquement Postgres/Qdrant avec l'app en exécution sur l'hôte.
Desktop (Tauri)
src-tauri/ - app desktop qui wrappe le frontend. Build avec pnpm tauri build. Configure src-tauri/tauri.conf.json pour l'URL server distante ou le bundle local.
Reverse proxy (Nginx)
Points clés :
- SSE : désactiver le buffering (
proxy_buffering off;,X-Accel-Buffering: nodéjà envoyé par le server/widget). - Timeout : LLM locaux lents →
proxy_read_timeoutélevé (≥ 600s, l'agent watchdog par défaut est 10 min). - Upload :
client_max_body_size≥ 100 MB (limite multer documents). - CORS : l'allowlist est gérée côté app (
ALLOWED_ORIGINS) ; le widget a un CORS dynamique per-origin (allowedOrigins).
Backup en production
- Crée une destination (
POST /api/backup-destinations). - Crée un job (
POST /api/backup-jobs) avec cronfrequency/time+retentionDays. - Vérifie avec
POST /backup-destinations/:id/testet un run manuelPOST /backup-jobs/:id/run. - Restore :
POST /api/backups/restore/:logId(dry-run conseillé avant :/restore/:logId/dry-run). Atomicité via psql--single-transaction.
Voir 05 - Feature Guide § Backup.
Sécurité de production
- Change le mot de passe admin bootstrap ; définis
ALLOW_REGISTRATION=falseen prod (self-registration fermée). DLP_ENABLEDdéfaut"true"en v0.22 - redaction input/output chat sur 10 patterns built-in (voir 05 - Feature Guide) ; utiliseDLP_BYPASS_ROLESpour exempter des rôles sélectionnés.- Active
audit_log_immutable(Enterprise) pour un audit append-only. - HTTPS terminé au reverse proxy ; headers
helmetactifs sur le server (relâchés sur le widget pour l'embedding). - Rate limit déjà configurés (voir 04 - API Reference § Rate limit) : la limite chat est désormais gérée par le budget tracker de l'agent (concurrence per-utilisateur, wallclock, token) plus le filet de protection global 200 req/min.
Cross-link
- Env complets : 03 - Configuration
- Développement : 10 - Development
- Troubleshooting deploy : 12 - Troubleshooting