b617487d0d
Export JSON workflows-n8n/chlova-alerts.v1.0.0.json (webhook → mail, formate par type d'alerte) + doc d'asset (palier privileged, rollback). ALERT_WEBHOOK_URL dans .env.example + compose (vide = log-only). need-review.md : alertes implémentées. Compose revalidé, 53 tests, 0 vuln. Palier de risque : privilégié (workflow envoie des mails) — désactivable via ALERT_WEBHOOK_URL vide ; non exécuté depuis le dépôt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
68 lines
3.2 KiB
Markdown
68 lines
3.2 KiB
Markdown
# 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.
|
|
|
|
## Alertes (Phase 3 — implémenté)
|
|
Stratégie anti-fatigue (`src/alerts/`) :
|
|
- **tentative bloquée** → alerte immédiate (`onBlockedAttempt`) ;
|
|
- **1ʳᵉ exécution d'un asset PROVISOIRE** → alerte immédiate (`onFirstProvisionalExec`) ;
|
|
- **digest quotidien** + **rappel J-1** → `startAlertScheduler` (`runAlertCycleOnce`).
|
|
|
|
Transport : `HttpAlertSender` POST vers `ALERT_WEBHOOK_URL` (webhook n8n) ; sans
|
|
URL, `NullAlertSender` log-only (fail-safe). Best-effort : une panne d'alerte ne
|
|
casse jamais l'agent. Le mail est envoyé par le workflow
|
|
`workflows-n8n/chlova-alerts.v1.0.0.json` (doc : `docs/assets/workflow-chlova-alerts.md`).
|
|
Le payload ne contient aucun secret.
|
|
|
|
## Reste à faire (Phase 4+)
|
|
- Auto-extension : CHLOVA génère commit + version + doc d'un asset **avant**
|
|
de le passer en need-review (Phase 4).
|