docs: documentation + gate de phase compose, fin Phase 2 (v0.14.0)

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 <noreply@anthropic.com>
This commit is contained in:
Kantin-Petit
2026-06-23 01:34:12 +02:00
parent a193b4e912
commit 52980837a9
6 changed files with 90 additions and 10 deletions
+7 -2
View File
@@ -18,7 +18,9 @@ MCP_N8N_AUTH_TOKEN= # SECRET — auth du serveur MCP n8n
# ── MCP Portainer (portainer/portainer-mcp) ──────────────────────────── # ── MCP Portainer (portainer/portainer-mcp) ────────────────────────────
PORTAINER_URL=https://portainer:9443 # interne uniquement PORTAINER_URL=https://portainer:9443 # interne uniquement
PORTAINER_MCP_AUTH_TOKEN= # SECRET — token Portainer à portée RESTREINTE 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 MCP_PORTAINER_URL=http://mcp-portainer:3000
# ── Surface Telegram (Phase 1) ───────────────────────────────────────── # ── Surface Telegram (Phase 1) ─────────────────────────────────────────
@@ -28,6 +30,9 @@ TELEGRAM_ALLOWED_USER_IDS= # liste d'IDs autorisés, séparés par vir
# ── Backend CHLOVA ───────────────────────────────────────────────────── # ── Backend CHLOVA ─────────────────────────────────────────────────────
CHLOVA_ENV=development # development | production CHLOVA_ENV=development # development | production
CHLOVA_LOG_LEVEL=info 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. # Phase 1 : aucun port publié (Telegram en long-polling). Renseigné en P3+ si API/UI.
# CHLOVA_HTTP_PORT=8080 # CHLOVA_HTTP_PORT=8080
+10
View File
@@ -6,6 +6,16 @@ incompatibles. Chaque ligne renvoie à un commit dédié (un artefact = un commi
## [Unreleased] ## [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 ## [0.13.0] — 2026-06-23
### Added ### Added
- `src/gatekeeper/review.ts` : `ReviewService` (approuver/refuser/lister), - `src/gatekeeper/review.ts` : `ReviewService` (approuver/refuser/lister),
+57
View File
@@ -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 <id>`, `/refuse <id>`, `/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).
+6 -4
View File
@@ -30,7 +30,9 @@
- Ceci fait l'objet d'un test dédié (`gatekeeper.test.ts`) : tentative de - Ceci fait l'objet d'un test dédié (`gatekeeper.test.ts`) : tentative de
reclassement → rejet. reclassement → rejet.
## Phase 1 (lecture seule) ## Application par phase
En Phase 1, **seuls les outils `reversible` (read-only) sont exposés** au LLM. - **Phase 1** (`CHLOVA_PHASE=1`, défaut) : seuls les outils `reversible`
Les outils mutants/`privileged` ne sont même pas branchés. Le gatekeeper et la (read-only) sont exposés ; les `privileged` ne sont pas branchés.
classification sont posés comme interfaces, prêts pour la Phase 2. - **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`.
+1
View File
@@ -91,6 +91,7 @@ services:
env_file: ../.env env_file: ../.env
environment: environment:
CHLOVA_ENV: ${CHLOVA_ENV:-production} 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} OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
MCP_N8N_URL: ${MCP_N8N_URL:-http://mcp-n8n:3000} MCP_N8N_URL: ${MCP_N8N_URL:-http://mcp-n8n:3000}
MCP_PORTAINER_URL: ${MCP_PORTAINER_URL:-http://mcp-portainer:3000} MCP_PORTAINER_URL: ${MCP_PORTAINER_URL:-http://mcp-portainer:3000}
+9 -4
View File
@@ -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/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/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/index.ts` | Bootstrap : config → verrou RO → logger → (P1) MCP + agent + Telegram. Healthcheck interne. |
| `src/llm/` | Client Ollama + boucle agent (v0.6.0). | | `src/llm/`, `src/agent/` | Client Ollama + boucle agent + prompt phase-aware. |
| `src/mcp/` | Registry MCP + readonly-filter (v0.7.0). | | `src/mcp/` | Registry MCP (`listReadOnlyTools` P1 / `listAllTools` P2) + readonly-filter. |
| `src/gatekeeper/` | Paliers de risque + table assets (interfaces P1, câblé P2). | | `src/gatekeeper/` | Paliers de risque, table assets SQLite (`repository`), `Gatekeeper`+`GatekeeperGuard`, `ReviewService` + cron (`review`). |
| `src/surfaces/` | Surface Telegram (v0.8.0). | | `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é ## Sécurité
- Secrets par référence uniquement, jamais loggés (`redactedConfig` + redact pino). - Secrets par référence uniquement, jamais loggés (`redactedConfig` + redact pino).