feat: workflow n8n d'alerte + doc, fin Phase 3 (v0.18.0)
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>
This commit is contained in:
@@ -38,5 +38,9 @@ CHLOVA_LOG_LEVEL=info
|
||||
# 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)
|
||||
# Alertes (Phase 3) : URL du webhook n8n qui envoie le mail (workflow
|
||||
# workflows-n8n/chlova-alerts.v1.0.0.json). Vide = alertes log-only (fail-safe).
|
||||
# Peut contenir un token de chemin → secret, jamais commité.
|
||||
ALERT_WEBHOOK_URL= # ex. http://n8n:5678/webhook/chlova-alert
|
||||
# Phase 1 : aucun port publié (Telegram en long-polling). Renseigné en P3+ si API/UI.
|
||||
# CHLOVA_HTTP_PORT=8080
|
||||
|
||||
@@ -6,6 +6,16 @@ incompatibles. Chaque ligne renvoie à un commit dédié (un artefact = un commi
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.18.0] — 2026-06-23 — fin Phase 3 (alertes)
|
||||
### Added
|
||||
- `workflows-n8n/chlova-alerts.v1.0.0.json` : workflow n8n webhook → mail (formate
|
||||
selon le type d'alerte). Exporté en JSON (le dépôt fait foi).
|
||||
- `docs/assets/workflow-chlova-alerts.md` : doc d'asset (rôle, IO, deps, sécurité,
|
||||
rollback) — palier `privileged`.
|
||||
### Changed
|
||||
- `.env.example` + compose : variable `ALERT_WEBHOOK_URL` (vide = log-only).
|
||||
- `docs/need-review.md` : section alertes marquée implémentée. Compose revalidé.
|
||||
|
||||
## [0.17.0] — 2026-06-23
|
||||
### Added
|
||||
- Config `ALERT_WEBHOOK_URL` (optionnel, traité comme secret) → `HttpAlertSender`,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
## CHLOVA Alerts
|
||||
|
||||
- **Type** : `workflow-n8n`
|
||||
- **Version** : `v1.0.0`
|
||||
- **Palier de risque** : `privileged` (envoie des mails / sortie externe)
|
||||
- **Statut (need-review)** : `n/a` (créé à la main, hors auto-extension)
|
||||
- **Lien commit** : voir tag `v0.18.0`
|
||||
- **Créé le** : 2026-06-23
|
||||
- **Fichier** : `workflows-n8n/chlova-alerts.v1.0.0.json`
|
||||
|
||||
### Rôle
|
||||
Reçoit les alertes du backend CHLOVA (webhook) et envoie un mail. Met en œuvre la
|
||||
stratégie anti-fatigue : tentative bloquée, 1ʳᵉ exécution provisoire, rappel J-1,
|
||||
digest quotidien (voir `docs/need-review.md`).
|
||||
|
||||
### Entrées / sorties
|
||||
- **Entrée** : `POST /webhook/chlova-alert`, corps JSON
|
||||
`{ source, ts, kind, ... }` (`kind` ∈ blocked_attempt | first_provisional_exec |
|
||||
countdown_j1 | daily_digest). Jamais de secret.
|
||||
- **Sortie** : un mail (sujet + corps formatés par le node *Format mail*).
|
||||
|
||||
### Dépendances
|
||||
- n8n (instance existante) + credential **SMTP** (`SMTP CHLOVA`, à créer dans n8n).
|
||||
- Backend : variable `ALERT_WEBHOOK_URL` = URL de ce webhook (par **référence**).
|
||||
|
||||
### Sécurité
|
||||
- Le webhook ne reçoit que des métadonnées (ids/types/statuts/dates).
|
||||
- Destinataire et identifiants SMTP vivent dans n8n (jamais dans le dépôt) :
|
||||
remplacer `toEmail` et le credential `smtp` après import.
|
||||
- Palier `privileged` : sortie mail externe.
|
||||
|
||||
### Rollback
|
||||
- Désactiver/supprimer le workflow dans n8n ; ou vider `ALERT_WEBHOOK_URL` côté
|
||||
backend → retour au log-only (`NullAlertSender`), sans rupture.
|
||||
- `git revert` du commit pour retirer l'export.
|
||||
|
||||
### Tests / vérification
|
||||
- Importer le JSON, brancher SMTP, exécuter un `POST` de test avec
|
||||
`{"kind":"daily_digest","blocked":1,"provisional":0,"items":[]}` → mail reçu.
|
||||
- Le dépôt fait foi : ré-exporter le workflow ici après toute modif (bump version).
|
||||
+13
-3
@@ -50,8 +50,18 @@ peut alors valoir `false`).
|
||||
- `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`.
|
||||
## 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).
|
||||
|
||||
@@ -85,6 +85,7 @@ services:
|
||||
environment:
|
||||
CHLOVA_ENV: ${CHLOVA_ENV:-production}
|
||||
CHLOVA_PHASE: ${CHLOVA_PHASE:-1} # 1 = lecture seule (défaut) ; 2 = écriture sous review
|
||||
ALERT_WEBHOOK_URL: ${ALERT_WEBHOOK_URL:-} # Phase 3 : vide = alertes log-only
|
||||
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||||
MCP_N8N_URL: ${MCP_N8N_URL} # endpoint MCP natif de n8n
|
||||
MCP_PORTAINER_URL: ${MCP_PORTAINER_URL:-http://mcp-portainer:3000}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "CHLOVA Alerts v1.0.0",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "chlova-alert",
|
||||
"responseMode": "onReceived",
|
||||
"options": {}
|
||||
},
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"name": "Webhook CHLOVA",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"position": [240, 300],
|
||||
"webhookId": "chlova-alert"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"language": "javaScript",
|
||||
"jsCode": "// Construit sujet + corps du mail selon le type d'alerte CHLOVA.\nconst a = $json;\nlet subject, lines = [];\nswitch (a.kind) {\n case 'blocked_attempt':\n subject = `[CHLOVA] Tentative BLOQUÉE : ${a.tool}`;\n lines = [`Asset: ${a.assetId}`, `Outil: ${a.tool}`, `Statut: ${a.status}`, 'Action: review requise (/approve ou /refuse).'];\n break;\n case 'first_provisional_exec':\n subject = `[CHLOVA] 1ère exécution (provisoire) : ${a.tool}`;\n lines = [`Asset: ${a.assetId}`, `Outil: ${a.tool}`, 'Sursis de 7 jours en cours.'];\n break;\n case 'countdown_j1':\n subject = `[CHLOVA] Rappel J-1 : ${a.items.length} asset(s) expirent bientôt`;\n lines = a.items.map(i => `${i.id} [${i.riskTier}] expire ${new Date(i.expiresAt).toISOString().slice(0,10)}`);\n break;\n case 'daily_digest':\n subject = `[CHLOVA] Digest : ${a.blocked} bloqué(s), ${a.provisional} provisoire(s)`;\n lines = a.items.map(i => `${i.id} [${i.riskTier}/${i.status}]`);\n break;\n default:\n subject = `[CHLOVA] Alerte: ${a.kind}`;\n lines = [JSON.stringify(a)];\n}\nreturn [{ json: { subject, text: lines.join('\\n') } }];"
|
||||
},
|
||||
"id": "22222222-2222-4222-8222-222222222222",
|
||||
"name": "Format mail",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [480, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fromEmail": "chlova@example.com",
|
||||
"toEmail": "REMPLACER_PAR_DESTINATAIRE",
|
||||
"subject": "={{ $json.subject }}",
|
||||
"text": "={{ $json.text }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"name": "Envoi mail",
|
||||
"type": "n8n-nodes-base.emailSend",
|
||||
"typeVersion": 2.1,
|
||||
"position": [720, 300],
|
||||
"credentials": {
|
||||
"smtp": { "id": "REMPLACER", "name": "SMTP CHLOVA" }
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook CHLOVA": { "main": [[{ "node": "Format mail", "type": "main", "index": 0 }]] },
|
||||
"Format mail": { "main": [[{ "node": "Envoi mail", "type": "main", "index": 0 }]] }
|
||||
},
|
||||
"settings": { "executionOrder": "v1" },
|
||||
"meta": { "chlova": { "asset": "workflow-chlova-alerts", "version": "1.0.0", "riskTier": "privileged" } }
|
||||
}
|
||||
Reference in New Issue
Block a user