e6edf1a8bc
Vue Review (liste assets, approuver/refuser + confirm, refresh, 401→logout). Backend sert le SPA same-origin (@fastify/static + fallback) si CHLOVA_WEB_ROOT. Dockerfile multi-stage build web+API (contexte racine), image embarque /app/web. Compose contexte .., image chlova/backend:0.2.0. 65 tests, 0 vuln, compose OK. Palier de risque : privilégié (surface exposée complète) — non déployée ; auth + CHLOVA_PHASE requis pour activer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
123 lines
4.9 KiB
YAML
123 lines
4.9 KiB
YAML
# 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 <image>:<tag>` puis remplacer par
|
|
# `<image>:<tag>@sha256:<digest>` 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
|
|
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+)
|
|
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:
|