# CHLOVA — stack d'orchestration. # Règles : une seule surface exposée (backend), tout le reste interne, images # épinglées (jamais :latest), secrets par référence (voir ../.env.example). # # ⚠️ Les tags ci-dessous sont des points d'épinglage à CONFIRMER avant tout # déploiement réel : `docker pull :` puis remplacer par # `:@sha256:` pour une immuabilité complète (docs/versioning.md). # Aucun déploiement réel n'est lancé depuis ce dépôt sans validation explicite. name: chlova services: # ── Ollama : proxy authentifié vers les modèles cloud (ollama.com) ────── ollama: image: ollama/ollama:0.6.8 # TODO épingler le digest restart: unless-stopped environment: # Clé du proxy cloud — injectée depuis .env, jamais en dur. OLLAMA_API_KEY: ${OLLAMA_API_KEY:?OLLAMA_API_KEY requis} OLLAMA_HOST: 0.0.0.0:11434 volumes: - ollama-data:/root/.ollama networks: - chlova-internal # joignable par le backend - chlova-egress # seul service autorisé à sortir # AUCUN port publié : Ollama n'a pas d'auth native, jamais exposé. # ── socket-proxy : accès Docker filtré (voir socket-proxy/README.md) ──── socket-proxy: image: tecnativa/docker-socket-proxy:0.3.0 # TODO épingler le digest restart: unless-stopped environment: # Phase 1 — LECTURE SEULE : lecture autorisée, mutation refusée. CONTAINERS: 1 IMAGES: 1 NETWORKS: 1 VOLUMES: 1 SERVICES: 1 TASKS: 1 NODES: 1 INFO: 1 VERSION: 1 POST: 0 # refuse toute mutation EXEC: 0 # refuse exec dans un conteneur AUTH: 0 SECRETS: 0 CONFIGS: 0 BUILD: 0 COMMIT: 0 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro # seul à monter le socket networks: - chlova-internal # AUCUN port publié. # ── MCP n8n : NATIF (instance n8n ≥ 2.18.4) — pas de conteneur dédié ───── # n8n expose son propre serveur MCP. À activer côté instance n8n (variable # d'env d'instance + activation par workflow), puis copier l'URL + le "MCP # Access Token" depuis n8n → Connection details. Le backend s'y connecte via # MCP_N8N_URL (réseau interne). n8n doit être attaché à chlova-internal. # ── MCP Portainer (portainer/portainer-mcp) — read-only en Phase 1 ────── mcp-portainer: image: portainer/portainer-mcp:0.6.0 # TODO confirmer tag/transport + épingler digest restart: unless-stopped environment: PORTAINER_URL: ${PORTAINER_URL} PORTAINER_MCP_AUTH_TOKEN: ${PORTAINER_MCP_AUTH_TOKEN:?requis} PORTAINER_READ_ONLY: ${PORTAINER_READ_ONLY:-true} # P1 : NE PAS passer à false # Docker via socket-proxy uniquement, jamais le socket brut. DOCKER_HOST: tcp://socket-proxy:2375 depends_on: - socket-proxy networks: - chlova-internal # AUCUN port publié. # ── Backend CHLOVA : SEULE surface, cerveau (boucle agent) ────────────── backend: build: context: .. # racine du dépôt (image = API + SPA web) dockerfile: orchestrator/Dockerfile image: chlova/backend:0.2.0 # tag versionné local (API+UI) restart: unless-stopped env_file: ../.env environment: CHLOVA_ENV: ${CHLOVA_ENV:-production} CHLOVA_PHASE: ${CHLOVA_PHASE:-1} # 1 = lecture seule (défaut) ; 2 = écriture sous review ALERT_WEBHOOK_URL: ${ALERT_WEBHOOK_URL:-} # Phase 3 : vide = alertes log-only CHLOVA_AUTOEXT_ENABLED: ${CHLOVA_AUTOEXT_ENABLED:-false} # Phase 5 : off par défaut CHLOVA_REPO_ROOT: ${CHLOVA_REPO_ROOT:-/app/repo} OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434} MCP_N8N_URL: ${MCP_N8N_URL} # endpoint MCP natif de n8n MCP_PORTAINER_URL: ${MCP_PORTAINER_URL:-http://mcp-portainer:3000} volumes: - chlova-data:/app/data # SQLite (table assets, P2+) # Auto-extension (Phase 5, off par défaut) : monter le dépôt git ici pour # que CHLOVA puisse committer les assets créés. # - /srv/chlova-repo:/app/repo depends_on: - ollama - mcp-portainer networks: - chlova-internal - traefik-public # API/UI exposée via Traefik (Phase 4) # Phase 4 : SEUL service exposé, via Traefik + TLS (jamais de port publié en # direct). L'API/UI ne s'active que si l'auth est configurée (.env). labels: traefik.enable: "true" traefik.docker.network: traefik-public traefik.http.routers.chlova.rule: Host(`${CHLOVA_DOMAIN:-chlova.example.com}`) traefik.http.routers.chlova.entrypoints: websecure traefik.http.routers.chlova.tls: "true" traefik.http.routers.chlova.tls.certresolver: le traefik.http.services.chlova.loadbalancer.server.port: "8080" networks: chlova-internal: internal: true # aucune route vers l'extérieur chlova-egress: driver: bridge # sortie contrôlée (Ollama → ollama.com), egress filtré côté hôte traefik-public: external: true # réseau Traefik existant du homelab volumes: ollama-data: chlova-data: