2d3c944699
Image portainer-mcp 0.6.0 (inexistant) -> 2.42.6. Passerelle HTTP :17717/mcp attend Bearer (secret passerelle) + X-Portainer-API-Key (clé API restreinte chlova) : ajout config.portainerApiKey + McpServerConfig.extraHeaders, backend envoie les deux. socket-proxy supprimé (plus de socket monté). Compose prod, .env.example, deploy.md à jour. Typecheck + 78 tests verts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016w5jRe87MGdd6AMvXQcHNi
138 lines
6.0 KiB
Markdown
138 lines
6.0 KiB
Markdown
# 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/<admin>/chlova.git
|
|
git push gitea main # auth : <admin> + 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` + clé API + secret de passerelle
|
|
1. **Users → Add user** : `chlova`, non-admin.
|
|
2. **Environments → local → Access** : rôle le plus bas suffisant. *(CE = RBAC
|
|
par environnement, pas par stack ; verrous additionnels :
|
|
`PORTAINER_READ_ONLY=true` côté serveur MCP **et** la clé API étant celle de
|
|
`chlova`, l'accès réel est borné à ce que `chlova` peut voir.)*
|
|
3. Connecté en `chlova` → **My account → Access tokens → Add token** →
|
|
`PORTAINER_API_KEY` (envoyé en `X-Portainer-API-Key`, cloisonne l'accès).
|
|
4. **`PORTAINER_MCP_AUTH_TOKEN`** = secret de **passerelle** au choix (chaîne
|
|
aléatoire) ; même valeur dans le stack `mcp-portainer` et côté backend.
|
|
|
|
### 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 -- <admin_user> '<mot_de_passe_fort>'
|
|
```
|
|
|
|
→ `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` | secret de passerelle (§3a.4) | **oui** |
|
|
| `PORTAINER_API_KEY` | clé API Portainer de chlova (§3a.3) | **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/<admin>/chlova.git` (clone interne) ou
|
|
`https://git.pogoo.app/<admin>/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).
|