feat: docker-compose stack CHLOVA (v0.4.0) — fin Phase 0
Stack complète : Ollama (proxy cloud, interne+egress, aucun port), socket-proxy (endpoints lecture seule), MCP n8n + Portainer (read-only), backend = seule surface (aucun port publié en P1, Telegram long-polling). Réseaux internal/egress, images épinglées (digests à confirmer), secrets par référence. `docker compose config` valide. Palier de risque : privilégié (déploiement infra) — non déployé, validation requise avant tout `up`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# 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 (czlonkowski/n8n-mcp) — read-only en Phase 1 ────────────────
|
||||
mcp-n8n:
|
||||
image: ghcr.io/czlonkowski/n8n-mcp:2.18.3 # TODO confirmer tag + épingler digest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MCP_MODE: http # transport HTTP (backend distant via réseau)
|
||||
MCP_AUTH_TOKEN: ${MCP_N8N_AUTH_TOKEN:?requis}
|
||||
N8N_API_URL: ${N8N_API_URL} # instance n8n existante (interne)
|
||||
N8N_API_KEY: ${N8N_API_KEY:?requis} # token n8n à portée RESTREINTE (lecture P1)
|
||||
networks:
|
||||
- chlova-internal
|
||||
# AUCUN port publié.
|
||||
|
||||
# ── 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: ../orchestrator # Dockerfile ajouté en Phase 1
|
||||
image: chlova/backend:0.1.0 # tag versionné local
|
||||
restart: unless-stopped
|
||||
env_file: ../.env
|
||||
environment:
|
||||
CHLOVA_ENV: ${CHLOVA_ENV:-production}
|
||||
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||||
MCP_N8N_URL: ${MCP_N8N_URL:-http://mcp-n8n:3000}
|
||||
MCP_PORTAINER_URL: ${MCP_PORTAINER_URL:-http://mcp-portainer:3000}
|
||||
volumes:
|
||||
- chlova-data:/app/data # SQLite (table assets, P2+)
|
||||
depends_on:
|
||||
- ollama
|
||||
- mcp-n8n
|
||||
- mcp-portainer
|
||||
networks:
|
||||
- chlova-internal
|
||||
# Phase 1 : surface Telegram en long-polling → AUCUN port publié.
|
||||
# Phases ultérieures (API/UI) : exposer UNIQUEMENT ce service derrière
|
||||
# Traefik + TLS et/ou VPN mesh. Voir infra/networks.md.
|
||||
|
||||
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
|
||||
|
||||
volumes:
|
||||
ollama-data:
|
||||
chlova-data:
|
||||
Reference in New Issue
Block a user