From 52980837a9fd69d11288e1c43b71fa12b5aaa9c1 Mon Sep 17 00:00:00 2001 From: Kantin-Petit Date: Tue, 23 Jun 2026 01:34:12 +0200 Subject: [PATCH] docs: documentation + gate de phase compose, fin Phase 2 (v0.14.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/need-review.md (cycle complet : états, sursis, gatekeeper, cron, review, rollback). CHLOVA_PHASE dans .env.example + compose (défaut 1). risk-tiers + README : application par phase. Compose revalidé, 45 tests, 0 vuln. Palier de risque : n/a (doc + config). Co-Authored-By: Claude Opus 4.8 --- .env.example | 9 +++++-- CHANGELOG.md | 10 +++++++ docs/need-review.md | 57 ++++++++++++++++++++++++++++++++++++++++ docs/risk-tiers.md | 10 ++++--- infra/docker-compose.yml | 1 + orchestrator/README.md | 13 ++++++--- 6 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 docs/need-review.md diff --git a/.env.example b/.env.example index cc3ce64..2297c96 100644 --- a/.env.example +++ b/.env.example @@ -18,7 +18,9 @@ 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 +# Phase 1 : DOIT rester true (le boot échoue sinon). Phase 2 : peut passer false +# pour autoriser les écritures Portainer (sous gatekeeper + review). +PORTAINER_READ_ONLY=true MCP_PORTAINER_URL=http://mcp-portainer:3000 # ── Surface Telegram (Phase 1) ───────────────────────────────────────── @@ -28,6 +30,9 @@ TELEGRAM_ALLOWED_USER_IDS= # liste d'IDs autorisés, séparés par vir # ── Backend CHLOVA ───────────────────────────────────────────────────── CHLOVA_ENV=development # development | production CHLOVA_LOG_LEVEL=info -CHLOVA_DB_PATH=./data/chlova.db # SQLite : table assets (need-review, P2+) +# Gate de phase : 1 = lecture seule (défaut, fail-safe) ; 2 = écriture sous +# gatekeeper + cycle need-review. Toute valeur autre que "2" retombe sur 1. +CHLOVA_PHASE=1 +CHLOVA_DB_PATH=./data/chlova.db # SQLite : table assets (need-review, Phase 2) # Phase 1 : aucun port publié (Telegram en long-polling). Renseigné en P3+ si API/UI. # CHLOVA_HTTP_PORT=8080 diff --git a/CHANGELOG.md b/CHANGELOG.md index 08a65fb..b39a9e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ incompatibles. Chaque ligne renvoie à un commit dédié (un artefact = un commi ## [Unreleased] +## [0.14.0] — 2026-06-23 — fin Phase 2 (écriture + need-review) +### Added +- `docs/need-review.md` : documentation complète du cycle (états, sursis, + gatekeeper, cron, review, gate de phase, rollback). +### Changed +- `.env.example` + `infra/docker-compose.yml` : variable `CHLOVA_PHASE` (défaut 1), + note `PORTAINER_READ_ONLY` selon phase. +- `docs/risk-tiers.md`, `orchestrator/README.md` : application par phase + (P1 read-only / P2 gatekeeper). Compose revalidé. + ## [0.13.0] — 2026-06-23 ### Added - `src/gatekeeper/review.ts` : `ReviewService` (approuver/refuser/lister), diff --git a/docs/need-review.md b/docs/need-review.md new file mode 100644 index 0000000..07420e2 --- /dev/null +++ b/docs/need-review.md @@ -0,0 +1,57 @@ +# Cycle "need review" (Phase 2) + +> Pièce maîtresse. Tout asset/op privilégié est sous review avant exécution. +> Voir `docs/risk-tiers.md` (paliers) et `docs/security.md` (sécurité). + +## Asset & états +Un asset (table SQLite `assets`, `src/gatekeeper/repository.ts`) suit : +- **PROVISOIRE** : créé et exécutable, sursis de 7 jours (réservé au palier + `reversible`). +- **APPROUVÉ** : review OK → permanent, plus d'alertes. +- **REFUSÉ** : review KO → désactivé. +- **BLOQUÉ** : sursis écoulé OU palier `privileged` (aucun sursis) → non + exécutable ; toute tentative lève une alerte. + +Champs : `id, type, version, risk_tier, status, created_at, expires_at, +exec_count, commit_link, doc_link`. + +## Règle de sursis (non négociable) +- `reversible` → PROVISOIRE 7 j (`createAsset` pose `expires_at = +7j`). +- `privileged` → **BLOQUÉ immédiat, aucun sursis** (`expires_at = null`). +- Le LLM ne peut jamais reclasser un asset (`assertNoEscalation` : + `privileged → reversible` interdit). Le palier vient des annotations MCP. + +## Gatekeeper (`src/gatekeeper/gatekeeper.ts`) +Vérifie le statut **avant chaque exécution** (`canExecute`) : +- lecture seule → autorisée, sans asset ; +- privilégié inconnu → enregistré **BLOQUÉ** + exécution refusée + hook d'alerte ; +- privilégié **APPROUVÉ** → exécuté, `exec_count` incrémenté ; +- REFUSÉ / BLOQUÉ / sursis expiré → refusé. + +Branché via `GatekeeperGuard` (interface `Guard` ; la boucle agent est inchangée). + +## Cron (`src/gatekeeper/review.ts`) +`startExpiryCron` passe horairement les PROVISOIRE échus en BLOQUÉ +(`expireProvisional`). `runExpiryOnce` = un passage (testable). + +## Review (humaine, hors LLM) +Commandes owner Telegram (`src/surfaces/commands.ts`) : +`/pending`, `/approve `, `/refuse `, `/help`. Le LLM n'y a pas accès : +seul l'humain change un statut. + +## Gate de phase +`CHLOVA_PHASE` (défaut 1). Phase 1 : aucun outil mutant exposé, `ReadOnlyGuard`. +Phase 2 : tous les outils exposés, `GatekeeperGuard`, cron + review actifs. +Passage à `CHLOVA_PHASE=2` requis pour toute écriture (et `PORTAINER_READ_ONLY` +peut alors valoir `false`). + +## Rollback +- Repasser `CHLOVA_PHASE=1` → écriture désactivée, retour au cerveau read-only. +- `git revert` du commit de l'asset fautif ; redeploy version N-1 (GitOps). +- Un asset REFUSÉ reste tracé (audit) ; suppression via op SQL manuelle si besoin. + +## Reste à faire (Phase 3+) +- Alertes mail via n8n (1ʳᵉ exéc provisoire, digest quotidien, rappel J-1, + alerte BLOQUÉ) — actuellement un simple hook de log `onBlockedAttempt`. +- Auto-extension : CHLOVA génère commit + version + doc d'un asset **avant** + de le passer en need-review (Phase 4). diff --git a/docs/risk-tiers.md b/docs/risk-tiers.md index 411b887..649d144 100644 --- a/docs/risk-tiers.md +++ b/docs/risk-tiers.md @@ -30,7 +30,9 @@ - Ceci fait l'objet d'un test dédié (`gatekeeper.test.ts`) : tentative de reclassement → rejet. -## Phase 1 (lecture seule) -En Phase 1, **seuls les outils `reversible` (read-only) sont exposés** au LLM. -Les outils mutants/`privileged` ne sont même pas branchés. Le gatekeeper et la -classification sont posés comme interfaces, prêts pour la Phase 2. +## Application par phase +- **Phase 1** (`CHLOVA_PHASE=1`, défaut) : seuls les outils `reversible` + (read-only) sont exposés ; les `privileged` ne sont pas branchés. +- **Phase 2** (`CHLOVA_PHASE=2`) : tous les outils exposés, mais chaque op + `privileged` passe par le **gatekeeper** (BLOQUÉ jusqu'à review). Voir + `docs/need-review.md`. diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 1e8da8c..2627f76 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -91,6 +91,7 @@ services: env_file: ../.env environment: CHLOVA_ENV: ${CHLOVA_ENV:-production} + CHLOVA_PHASE: ${CHLOVA_PHASE:-1} # 1 = lecture seule (défaut) ; 2 = écriture sous review 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} diff --git a/orchestrator/README.md b/orchestrator/README.md index b18ab4e..c28cde0 100644 --- a/orchestrator/README.md +++ b/orchestrator/README.md @@ -23,10 +23,15 @@ npm test | `src/config.ts` | Config **fail-closed** (zod). Refuse de démarrer si secret manquant. `assertReadOnlyPhase()` verrouille la lecture seule P1. `redactedConfig()` masque les secrets. | | `src/audit/log.ts` | Logger pino + journal d'audit des exécutions d'outils. | | `src/index.ts` | Bootstrap : config → verrou RO → logger → (P1) MCP + agent + Telegram. Healthcheck interne. | -| `src/llm/` | Client Ollama + boucle agent (v0.6.0). | -| `src/mcp/` | Registry MCP + readonly-filter (v0.7.0). | -| `src/gatekeeper/` | Paliers de risque + table assets (interfaces P1, câblé P2). | -| `src/surfaces/` | Surface Telegram (v0.8.0). | +| `src/llm/`, `src/agent/` | Client Ollama + boucle agent + prompt phase-aware. | +| `src/mcp/` | Registry MCP (`listReadOnlyTools` P1 / `listAllTools` P2) + readonly-filter. | +| `src/gatekeeper/` | Paliers de risque, table assets SQLite (`repository`), `Gatekeeper`+`GatekeeperGuard`, `ReviewService` + cron (`review`). | +| `src/surfaces/` | Surface Telegram + commandes de review owner (`commands`). | + +## Phases +- `CHLOVA_PHASE=1` (défaut) : lecture seule, `ReadOnlyGuard`, aucun outil mutant. +- `CHLOVA_PHASE=2` : écriture sous gatekeeper + cycle need-review (cron + commandes + `/pending`, `/approve`, `/refuse`). Voir `../docs/need-review.md`. ## Sécurité - Secrets par référence uniquement, jamais loggés (`redactedConfig` + redact pino).