commit a3012815944c858c2fad7db9955d3fbb43a6e729 Author: Kantin-Petit Date: Tue Jun 23 00:57:23 2026 +0200 chore: scaffold dépôt CHLOVA (v0.1.0) Pose le socle méta : CLAUDE.md (architecture cible + règles non négociables versioning/sécurité/risque), README, CHANGELOG, .env.example (références secrets only), .gitignore, squelette d'arborescence. Co-Authored-By: Claude Opus 4.8 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..cd44317 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "code-review@claude-plugins-official": true + } +} diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cc3ce64 --- /dev/null +++ b/.env.example @@ -0,0 +1,33 @@ +# CHLOVA — variables d'environnement (TEMPLATE) +# Copier en `.env` et renseigner. NE JAMAIS committer `.env` ni de vraie valeur. +# L'agent ne voit jamais ces secrets en clair : il manipule des références. + +# ── Ollama (proxy cloud) ─────────────────────────────────────────────── +# Le conteneur Ollama local proxifie vers ollama.com. Egress doit autoriser +# ollama.com. Joignable uniquement sur le réseau Docker interne. +OLLAMA_BASE_URL=http://ollama:11434 +OLLAMA_API_KEY= # SECRET — clé API Ollama cloud +OLLAMA_MODEL=qwen3:cloud # modèle cloud (suffixe :cloud), tool-calling + +# ── MCP n8n (czlonkowski/n8n-mcp) ────────────────────────────────────── +N8N_API_URL=http://n8n:5678 # interne uniquement +N8N_API_KEY= # SECRET — token n8n à portée RESTREINTE +MCP_N8N_URL=http://mcp-n8n:3000 # endpoint du serveur MCP n8n (interne) +MCP_N8N_AUTH_TOKEN= # SECRET — auth du serveur MCP n8n + +# ── MCP Portainer (portainer/portainer-mcp) ──────────────────────────── +PORTAINER_URL=https://portainer:9443 # interne uniquement +PORTAINER_MCP_AUTH_TOKEN= # SECRET — token Portainer à portée RESTREINTE +PORTAINER_READ_ONLY=true # Phase 1 : LECTURE SEULE — ne pas passer à false +MCP_PORTAINER_URL=http://mcp-portainer:3000 + +# ── Surface Telegram (Phase 1) ───────────────────────────────────────── +TELEGRAM_BOT_TOKEN= # SECRET — token du bot +TELEGRAM_ALLOWED_USER_IDS= # liste d'IDs autorisés, séparés par virgule + +# ── Backend CHLOVA ───────────────────────────────────────────────────── +CHLOVA_ENV=development # development | production +CHLOVA_LOG_LEVEL=info +CHLOVA_DB_PATH=./data/chlova.db # SQLite : table assets (need-review, P2+) +# Phase 1 : aucun port publié (Telegram en long-polling). Renseigné en P3+ si API/UI. +# CHLOVA_HTTP_PORT=8080 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c3d8d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Secrets — JAMAIS commités +.env +.env.* +!.env.example +*.key +*.pem +secrets/ + +# Node +node_modules/ +dist/ +*.tsbuildinfo +coverage/ +.vitest/ + +# Données runtime +data/ +*.db +*.sqlite +*.sqlite3 + +# Logs +*.log +logs/ + +# OS / éditeurs +.DS_Store +Thumbs.db +.idea/ +.vscode/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cd873a1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +Format [Keep a Changelog](https://keepachangelog.com/), versioning [SemVer](https://semver.org/). +Tant que le projet est en `0.x`, chaque mineur peut introduire des changements +incompatibles. Chaque ligne renvoie à un commit dédié (un artefact = un commit). + +## [Unreleased] + +## [0.1.0] — 2026-06-23 +### Added +- Scaffold du dépôt : `CLAUDE.md` (architecture + règles non négociables), + `README.md`, `CHANGELOG.md`, `.env.example`, `.gitignore`. +- Squelette d'arborescence : `docs/`, `infra/`, `orchestrator/`, `workflows-n8n/`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2e8d077 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,92 @@ +# CHLOVA — guide projet (CLAUDE.md) + +CHLOVA est un assistant personnel (inspiré de Jarvis). **Ce n'est pas un produit +from-scratch** : c'est une **couche d'orchestration** au-dessus d'un homelab +existant. Le cerveau est un LLM en boucle tool-calling : +`comprendre → choisir un outil MCP → agir → observer → répondre`. + +## Substrat existant (réutiliser, ne pas réinventer) +- Debian + Docker + Traefik. +- **n8n** (automations) — joignable via son MCP. +- **Portainer** (déploiement de conteneurs) — joignable via son MCP. +- **Ollama** : conteneur de la stack, joignable par l'orchestrateur sur le réseau + Docker interne (`http://ollama:11434`). On utilise des **modèles cloud** + (suffixe `:cloud`) : le conteneur Ollama local agit en **proxy authentifié** + vers l'infra distante d'Ollama. Transparent pour l'orchestrateur (il croit + parler à du Ollama local). Auth via `OLLAMA_API_KEY` (coffre, jamais en dur). + Conséquences : **pas de GPU requis**, mais les requêtes **sortent vers + `ollama.com`** (egress doit l'autoriser) et il **n'existe pas de palier + "local/privé"**. + +## Architecture cible +- Tout vit sur le serveur : orchestrateur LLM + Ollama (proxy cloud) + outils MCP. +- **Une seule surface exposée : le backend CHLOVA.** Lui seul contacte Ollama, + n8n et Portainer, sur le réseau Docker interne. Aucune brique interne n'est + jamais joignable directement de l'extérieur. +- Le mobile est un **client léger** du backend CHLOVA, rien d'autre. +- Outils MCP : MCP n8n + MCP Portainer. +- Surfaces : **texte d'abord** (Telegram/Signal = nativement mobile, sans + exposition), **voix ensuite** (STT + wake-word + TTS). La voix vient **après** + un cerveau fiable. +- Auto-extension : si une demande n'est pas réalisable avec les outils existants, + CHLOVA les crée lui-même en état "need review". + +## Stack retenue (Phases 0–1) +- Orchestrateur : **TypeScript + Node 22**, MCP SDK officiel + `@modelcontextprotocol/sdk` (client). +- Backend/API : **Fastify**. +- LLM : client Ollama `/api/chat` (tool-calling natif), modèles `:cloud`. +- Surface texte Phase 1 : **bot Telegram** (long-polling → zéro port ouvert). +- MCP : conteneurs `portainer/portainer-mcp` + `czlonkowski/n8n-mcp`, tags + épinglés, **read-only en Phase 1**. +- Tests : **Vitest** (gatekeeper + scoping read-only). +- État assets : **SQLite** (table posée dès P0/P1, câblée en P2+). + +## Versioning & documentation (non négociable) +Git est la **source unique de vérité**. Tout artefact créé/modifié — workflow +n8n, stack/compose Docker, code orchestrateur, outil — doit être : +- **Versionné** : commit dédié + semver (`vMAJEUR.MINEUR.PATCH`). Chaque + création/modif = un commit clair + bump. `CHANGELOG.md` tenu à jour. +- **Documenté** : rôle, entrées/sorties, dépendances, palier de risque, rollback. + Un artefact non documenté est **incomplet**. +- Images Docker : tags **épinglés** et versionnés (jamais `:latest`). +- Workflows n8n : exportés en JSON dans `workflows-n8n/` (le dépôt fait foi). + +Rien n'est "terminé" tant qu'il n'est pas **à la fois versionné ET documenté**. + +## Accès mobile & exposition (non négociable) +- Seul le backend CHLOVA est exposé, derrière auth + TLS. +- Voie mobile : surface Telegram/Signal (distante par nature, zéro port ouvert) + ET/OU VPN mesh (Tailscale/Wireguard) + Traefik/TLS pour une API/UI CHLOVA. +- Ollama, n8n, Portainer n'écoutent **que** sur le réseau Docker interne. + Ollama n'a **aucune auth native** — ne jamais l'exposer. + +## Paliers de risque (règle non négociable) +- **Réversible / lecture seule** → 7 jours de sursis PROVISOIRE autorisés. +- **Privilégié / destructeur** → **aucun sursis** : BLOQUÉ jusqu'à review. + (déploiement Portainer, montage de volume hôte, accès secrets, suppression, + exec dans un conteneur, etc.) +- Le LLM **ne doit jamais** pouvoir reclasser un asset privilégié en réversible. + +## Cycle de vie "need review" (Phase 2+, interfaces posées en P1) +États d'un asset créé par CHLOVA : PROVISOIRE (7 j) → APPROUVÉ / REFUSÉ / BLOQUÉ. +Table assets : `id, type, version, palier_risque, statut, created_at, expires_at, +exec_count, lien_commit, lien_doc`. Cron horaire PROVISOIRE→BLOQUÉ ; **gatekeeper** +qui vérifie le statut **avant chaque exécution**. + +## Sécurité (non négociable — risque n°1 : prompt injection) +- Accès quasi-root via Portainer : **socket-proxy Docker obligatoire**, tokens + Portainer/n8n à portée restreinte, réseau dédié à l'agent, **egress filtré** + (autoriser `ollama.com`). +- L'agent ne voit **jamais** les secrets en clair : il manipule des références. + Tous les secrets via env/coffre, jamais en dur ; `.env.example` fourni. +- Toute opération mutante est **audit-loggée**. +- Déploiements de stacks en **GitOps** quand possible (rollback + audit gratuits). + +## Périmètre actuel +- **Phase 0** : socle (structure, CLAUDE.md, config, sécurité, conventions, + docker-compose stack avec Ollama interne + backend seule surface). +- **Phase 1** : cerveau **lecture seule** (orchestrateur LLM via Ollama cloud + + MCP n8n + MCP Portainer read-only + surface texte). +- **Phases 2+ NON implémentées** ici : écriture, need-review, voix, auto-extension. + On pose seulement les interfaces/abstractions. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e163c77 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# CHLOVA + +Assistant personnel (style Jarvis). **Couche d'orchestration** au-dessus d'un +homelab Debian + Docker + Traefik existant. Le cerveau est un LLM en boucle +tool-calling qui agit via des outils MCP (n8n, Portainer) et parle aux modèles +Ollama **cloud**. + +> Voir [`CLAUDE.md`](./CLAUDE.md) pour l'architecture et les règles non négociables. + +## État +- **Phase 0** — socle (en cours) : structure, sécurité, conventions, compose. +- **Phase 1** — cerveau **lecture seule** : orchestrateur + MCP read-only + texte. +- Phases 2+ : non implémentées (écriture, need-review, voix, auto-extension). + +## Structure +| Dossier | Rôle | +|---|---| +| `docs/` | Architecture, sécurité, versioning, paliers de risque, gabarit d'asset | +| `infra/` | docker-compose de la stack + socket-proxy + notes réseau | +| `orchestrator/` | Le cerveau (TypeScript/Node, Fastify) | +| `workflows-n8n/` | Exports JSON des workflows (le dépôt fait foi) | + +## Démarrage (dev) +À compléter en fin de Phase 1. Pré-requis : Node 22, Docker. Copier +`.env.example` → `.env`, renseigner les secrets (jamais commités). + +## Sécurité +Seul le backend CHLOVA est exposé. Ollama/n8n/Portainer restent sur le réseau +Docker interne. Secrets via env/coffre uniquement. Voir [`docs/security.md`]. diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/infra/.gitkeep b/infra/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workflows-n8n/.gitkeep b/workflows-n8n/.gitkeep new file mode 100644 index 0000000..e69de29