# Déploiement CHLOVA (Phase 1 — lecture seule, GitOps Portainer) Mise en production réelle sur le homelab. **Tout sur l'hôte `local`** (environnement Portainer endpoint 3), aux côtés du serveur Portainer et de Traefik. Pile dédiée à CHLOVA : | Stack | État | Rôle | |---|---|---| | `gitea` (endpoint 3) | **déployé** | dépôt git source du GitOps (`git.pogoo.app`) | | `n8n-chlova` (endpoint 3) | **déployé** | n8n dédié à CHLOVA (`n8n-chlova.pogoo.app`) | | `chlova` (endpoint 3) | à déployer | backend + ollama + sidecars (`chlova.pogoo.app`) | > **Modèle de risque.** Phase 1 = lecture seule : `CHLOVA_PHASE=1`, > `PORTAINER_READ_ONLY=true`, MCP read-only filtré. Aucune écriture branchée. > Les secrets de CHLOVA ne transitent **jamais** par l'agent : ils sont saisis > par l'opérateur dans les variables de stack Portainer (UI) — voir §4. Réseau commun : `proxy` (external) — Traefik, Portainer, gitea, n8n-chlova et le backend CHLOVA y sont tous attachés, donc joignables **en interne** par nom de conteneur. Resolver TLS : `letsencrypt`. Entrypoint : `websecure`. Compose de prod : [`infra/docker-compose.prod.yml`](../infra/docker-compose.prod.yml). Build de l'image : **par Portainer sur `local`** (GitOps : clone du dépôt gitea + `docker compose build`). Contexte de build = racine du dépôt. --- ## 0. DNS (préalable) Faire pointer vers l'IP de l'hôte `local` (la même que `traefik.pogoo.app`) : `git.pogoo.app`, `n8n-chlova.pogoo.app`, `chlova.pogoo.app`. Sans ça, Traefik ne peut pas émettre les certificats letsencrypt ni router. ## 1. Gitea — admin + dépôt (UI, une fois) `gitea` est déployé, installeur **ouvert** (tu crées le 1er compte = admin). 1. Ouvrir `https://git.pogoo.app` → compléter l'install (DB = SQLite déjà réglée) → créer le compte admin. 2. **New repository** : `chlova` (privé). 3. **Settings → Applications → Generate token** (scope `write:repository`) : sert à pousser le code (§2) et au clone GitOps par Portainer (§4). > L'enregistrement public est désactivé (`DISABLE_REGISTRATION=true`) : seul > l'admin existe. ## 2. Pousser le dépôt CHLOVA dans gitea Le code est sur le poste de build. Ajouter le remote gitea et pousser `main` : ```bash git remote add gitea https://git.pogoo.app//chlova.git git push gitea main # auth : + token de l'étape 1.3 ``` ## 3. Users restreints `chlova` (UI) > Le MCP Portainer n'expose pas la gestion users/tokens : étapes UI. Principe : > CHLOVA n'accède qu'à ses ressources, tokens à portée minimale. ### 3a. Portainer — user `chlova` + token 1. **Users → Add user** : `chlova`, non-admin. 2. **Environments → local → Access** : rôle le plus bas suffisant. *(CE = RBAC par environnement, pas par stack ; le vrai verrou Phase 1 reste `PORTAINER_READ_ONLY=true`.)* 3. Connecté en `chlova` → **My account → Access tokens → Add token** → `PORTAINER_MCP_AUTH_TOKEN`. ### 3b. n8n-chlova — MCP token 1. `https://n8n-chlova.pogoo.app` → créer le compte propriétaire. 2. Activer le serveur **MCP natif**, générer le **MCP Access Token** → `MCP_N8N_AUTH_TOKEN`. Endpoint interne : `http://n8n-chlova:5678/mcp-server/http`. ## 4. Secrets login UI (générés, jamais commités) ```bash cd orchestrator npm run provision-auth -- '' ``` → `CHLOVA_ADMIN_PASSWORD_HASH`, `CHLOVA_TOTP_SECRET`, `CHLOVA_JWT_SECRET` + `otpauth://…` (scanner dans l'app TOTP). À saisir dans les variables de stack (§5). ## 5. Déploiement du stack `chlova` `infra/docker-compose.prod.yml` n'a **aucun** `env_file` : toutes les variables sont des **variables de stack** Portainer (les secrets y sont saisis par l'opérateur, jamais par l'agent). | Variable | Valeur | Secret ? | |---|---|---| | `CHLOVA_DOMAIN` | `chlova.pogoo.app` | non | | `CHLOVA_PHASE` | `1` | non | | `PORTAINER_READ_ONLY` | `true` | non | | `PORTAINER_URL` | `http://portainer:9000` (défaut, interne) | non | | `OLLAMA_API_KEY` | clé Ollama cloud | **oui** | | `OLLAMA_MODEL` | `qwen3:cloud` | non | | `MCP_N8N_AUTH_TOKEN` | token MCP n8n-chlova (§3b) | **oui** | | `PORTAINER_MCP_AUTH_TOKEN` | token user chlova (§3a) | **oui** | | `CHLOVA_ADMIN_USER` | ex. `kantin` | non | | `CHLOVA_ADMIN_PASSWORD_HASH` | (§4) | **oui** | | `CHLOVA_TOTP_SECRET` | (§4) | **oui** | | `CHLOVA_JWT_SECRET` | (§4) | **oui** | **Stacks → Add stack → Git repository** : - Repository URL = `http://gitea:3000//chlova.git` (clone interne) ou `https://git.pogoo.app//chlova.git`. - Authentication = user gitea + token (§1.3). - Reference = `refs/heads/main` ; Compose path = `infra/docker-compose.prod.yml`. - Environment variables = tableau ci-dessus. - **Deploy**. Portainer clone, build l'image et lance le stack. ## 6. Vérification 1. Stack `chlova` *running* ; conteneurs `backend`, `ollama`, `mcp-portainer`, `socket-proxy` *up*. 2. Logs `backend` : `API/UI activée (auth configurée)` + `healthcheck interne prêt`. Pas d'erreur fail-closed. 3. `https://chlova.pogoo.app` → page login (mot de passe + TOTP). 4. Connexion → Chat répond ; outils MCP read-only (n8n + Portainer) listés. ## 7. Rollback GitOps : revert du commit compose + redeploy, ou **Stacks → chlova → Stop/Remove**. Volume `chlova-data` (SQLite) persiste ; le supprimer pour repartir de zéro. ## 8. Passage Phase 2 (plus tard) Écriture sous gatekeeper + need-review : `CHLOVA_PHASE=2` et `PORTAINER_READ_ONLY=false` (mutations alors filtrées par gatekeeper + paliers de risque). Uniquement après validation du cerveau en lecture seule. Voir [`docs/need-review.md`](./need-review.md).