feat: secrets-guardian refonte, supervisor + scripts brain-watch/notify, monitoring Telegram

This commit is contained in:
2026-03-14 08:41:09 +01:00
parent 9a2ed607de
commit 65ded4cc9d
9 changed files with 871 additions and 1 deletions

58
scripts/brain-notify.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
# brain-notify.sh — Canal Telegram du SUPERVISOR
# Usage: brain-notify.sh "MESSAGE" [urgent|update|info]
# urgent → 🔴 notification sonore — interruption humaine
# update → ✅ notification silencieuse — info non bloquante
# info → 💬 notification silencieuse — log passif
#
# Token lu depuis MYSECRETS — jamais hardcodé.
set -euo pipefail
MYSECRETS="${BRAIN_ROOT:-$HOME/Dev/Docs}/MYSECRETS"
if [[ ! -f "$MYSECRETS" ]]; then
echo "[brain-notify] ERREUR : MYSECRETS introuvable à $MYSECRETS" >&2
exit 1
fi
# Lire token + chat_id depuis MYSECRETS (source .env style)
TOKEN=$(grep '^BRAIN_TELEGRAM_TOKEN=' "$MYSECRETS" | cut -d= -f2-)
CHAT_ID=$(grep '^BRAIN_TELEGRAM_CHAT_ID=' "$MYSECRETS" | cut -d= -f2-)
if [[ -z "$TOKEN" || -z "$CHAT_ID" ]]; then
echo "[brain-notify] ERREUR : BRAIN_TELEGRAM_TOKEN ou BRAIN_TELEGRAM_CHAT_ID vide dans MYSECRETS" >&2
exit 1
fi
MESSAGE="${1:-}"
LEVEL="${2:-info}"
if [[ -z "$MESSAGE" ]]; then
echo "[brain-notify] ERREUR : message vide" >&2
exit 1
fi
# Préfixe selon le niveau
case "$LEVEL" in
urgent) PREFIX="🔴 *BRAIN ESCALADE*" ; SILENT=false ;;
update) PREFIX="✅ *BRAIN UPDATE*" ; SILENT=true ;;
info) PREFIX="💬 *BRAIN*" ; SILENT=true ;;
*) PREFIX="💬 *BRAIN*" ; SILENT=true ;;
esac
FULL_MESSAGE="${PREFIX}
${MESSAGE}
_$(date '+%Y-%m-%d %H:%M')_"
# Envoi Telegram
DISABLE_NOTIFICATION=$( [[ "$SILENT" == "true" ]] && echo "true" || echo "false" )
curl -s -X POST "https://api.telegram.org/bot${TOKEN}/sendMessage" \
-d chat_id="$CHAT_ID" \
-d text="$FULL_MESSAGE" \
-d parse_mode="Markdown" \
-d disable_notification="$DISABLE_NOTIFICATION" \
> /dev/null
echo "[brain-notify] [$LEVEL] envoyé"

68
scripts/brain-watch-local.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/bin/bash
# brain-watch-local.sh — Daemon SUPERVISOR local (desktop)
# Surveille BRAIN-INDEX.md via inotifywait (instant, sans polling)
# Lance en arrière-plan : nohup brain-watch-local.sh >> ~/brain-watch.log 2>&1 &
#
# Détecte :
# - Nouveau Claim ouvert → notify update
# - Claim fermé → notify info
# - Nouveau Signal → notify selon criticité
# - Condition d'escalade → notify urgent
set -euo pipefail
BRAIN_ROOT="${BRAIN_ROOT:-$HOME/Dev/Docs}"
BRAIN_INDEX="$BRAIN_ROOT/BRAIN-INDEX.md"
NOTIFY="$BRAIN_ROOT/scripts/brain-notify.sh"
LOG_PREFIX="[brain-watch-local]"
if [[ ! -f "$BRAIN_INDEX" ]]; then
echo "$LOG_PREFIX ERREUR : BRAIN-INDEX.md introuvable à $BRAIN_INDEX" >&2
exit 1
fi
if [[ ! -x "$NOTIFY" ]]; then
chmod +x "$NOTIFY"
fi
echo "$LOG_PREFIX Démarré — surveillance de $BRAIN_INDEX"
# Snapshot initial pour détecter les diffs
snapshot_claims() {
grep -c '^\|' "$BRAIN_INDEX" 2>/dev/null || echo 0
}
PREV_HASH=$(md5sum "$BRAIN_INDEX" | cut -d' ' -f1)
PREV_CLAIMS=$(grep -v '^\*Aucun claim' "$BRAIN_INDEX" | grep -c '^\| sess-' 2>/dev/null || echo 0)
inotifywait -m -e close_write,moved_to "$BRAIN_INDEX" 2>/dev/null | while read -r _dir _event _file; do
NEW_HASH=$(md5sum "$BRAIN_INDEX" | cut -d' ' -f1)
[[ "$NEW_HASH" == "$PREV_HASH" ]] && continue
PREV_HASH="$NEW_HASH"
NEW_CLAIMS=$(grep -v '^\*Aucun claim' "$BRAIN_INDEX" | grep -c '^\| sess-' 2>/dev/null || echo 0)
# Nouveau claim détecté
if [[ "$NEW_CLAIMS" -gt "$PREV_CLAIMS" ]]; then
SESS=$(grep '^\| sess-' "$BRAIN_INDEX" | tail -1 | awk -F'|' '{print $2}' | xargs)
"$NOTIFY" "Nouvelle session détectée\n*Session :* \`$SESS\`\nVérifier les claims actifs dans BRAIN-INDEX.md" "update"
echo "$LOG_PREFIX Nouveau claim : $SESS"
fi
# Claim fermé
if [[ "$NEW_CLAIMS" -lt "$PREV_CLAIMS" ]]; then
"$NOTIFY" "Session fermée — claim libéré\nClaims actifs restants : $NEW_CLAIMS" "info"
echo "$LOG_PREFIX Claim fermé — claims restants : $NEW_CLAIMS"
fi
PREV_CLAIMS="$NEW_CLAIMS"
# Détecter signaux BLOCKED_ON (escalade potentielle)
if grep -q 'BLOCKED_ON' "$BRAIN_INDEX" 2>/dev/null; then
BLOCKED=$(grep 'BLOCKED_ON' "$BRAIN_INDEX" | head -1)
"$NOTIFY" "Conflit détecté entre sessions\n$BLOCKED\nIntervention requise." "urgent"
echo "$LOG_PREFIX ESCALADE : BLOCKED_ON détecté"
fi
done

75
scripts/brain-watch-vps.sh Executable file
View File

@@ -0,0 +1,75 @@
#!/bin/bash
# brain-watch-vps.sh — Daemon SUPERVISOR VPS
# Clone le repo brain depuis Gitea, poll toutes les 30s
# Détecte les changements dans BRAIN-INDEX.md → notifie via Telegram
#
# Setup VPS (une seule fois) :
# 1. Copier ce script sur le VPS : scp brain-watch-vps.sh root@VPS:/home/tetardtek/brain-watch/
# 2. Copier brain-notify.sh aussi
# 3. Cloner le brain : git clone git@git.tetardtek.com:Tetardtek/brain.git /home/tetardtek/brain-watch/brain
# 4. Copier MYSECRETS sur le VPS : scp MYSECRETS root@VPS:/home/tetardtek/brain-watch/
# 5. Installer le service systemd : install-brain-watch-vps.sh
# 6. systemctl start brain-watch && systemctl enable brain-watch
set -euo pipefail
WATCH_ROOT="/home/tetardtek/brain-watch"
BRAIN_INDEX="$WATCH_ROOT/brain/BRAIN-INDEX.md"
NOTIFY="$WATCH_ROOT/brain-notify.sh"
BRAIN_ROOT="$WATCH_ROOT" # pour brain-notify.sh — lit MYSECRETS ici
POLL_INTERVAL=30
LOG_PREFIX="[brain-watch-vps]"
export BRAIN_ROOT
if [[ ! -d "$WATCH_ROOT/brain" ]]; then
echo "$LOG_PREFIX ERREUR : brain non cloné. Lancer : git clone git@git.tetardtek.com:Tetardtek/brain.git $WATCH_ROOT/brain" >&2
exit 1
fi
if [[ ! -x "$NOTIFY" ]]; then
chmod +x "$NOTIFY"
fi
echo "$LOG_PREFIX Démarré — poll toutes les ${POLL_INTERVAL}s"
PREV_HASH=$(md5sum "$BRAIN_INDEX" 2>/dev/null | cut -d' ' -f1 || echo "")
PREV_CLAIMS=$(grep -v '^\*Aucun claim' "$BRAIN_INDEX" 2>/dev/null | grep -c '^\| sess-' || echo 0)
while true; do
sleep "$POLL_INTERVAL"
# Pull silencieux
git -C "$WATCH_ROOT/brain" pull --quiet --ff-only 2>/dev/null || {
echo "$LOG_PREFIX WARNING : git pull échoué"
continue
}
NEW_HASH=$(md5sum "$BRAIN_INDEX" | cut -d' ' -f1)
[[ "$NEW_HASH" == "$PREV_HASH" ]] && continue
PREV_HASH="$NEW_HASH"
echo "$LOG_PREFIX Changement détecté dans BRAIN-INDEX.md"
NEW_CLAIMS=$(grep -v '^\*Aucun claim' "$BRAIN_INDEX" | grep -c '^\| sess-' 2>/dev/null || echo 0)
if [[ "$NEW_CLAIMS" -gt "$PREV_CLAIMS" ]]; then
SESS=$(grep '^\| sess-' "$BRAIN_INDEX" | tail -1 | awk -F'|' '{print $2}' | xargs)
"$NOTIFY" "Nouvelle session détectée\n*Session :* \`$SESS\`" "update"
echo "$LOG_PREFIX Nouveau claim : $SESS"
fi
if [[ "$NEW_CLAIMS" -lt "$PREV_CLAIMS" ]]; then
"$NOTIFY" "Session fermée — claim libéré\nClaims actifs restants : $NEW_CLAIMS" "info"
echo "$LOG_PREFIX Claim fermé"
fi
PREV_CLAIMS="$NEW_CLAIMS"
if grep -q 'BLOCKED_ON' "$BRAIN_INDEX" 2>/dev/null; then
BLOCKED=$(grep 'BLOCKED_ON' "$BRAIN_INDEX" | head -1)
"$NOTIFY" "Conflit inter-sessions (VPS)\n$BLOCKED\nIntervention requise." "urgent"
echo "$LOG_PREFIX ESCALADE : BLOCKED_ON"
fi
done

42
scripts/get-telegram-chatid.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
# get-telegram-chatid.sh — Récupère le chat_id Telegram et l'écrit dans MYSECRETS
# NE JAMAIS afficher la valeur dans le terminal — écriture directe dans MYSECRETS
#
# Prérequis : avoir envoyé /start au bot sur Telegram
set -euo pipefail
MYSECRETS="${BRAIN_ROOT:-$HOME/Dev/Docs}/MYSECRETS"
TOKEN=$(grep '^BRAIN_TELEGRAM_TOKEN=' "$MYSECRETS" | cut -d= -f2-)
if [[ -z "$TOKEN" ]]; then
echo "ERREUR : BRAIN_TELEGRAM_TOKEN vide dans MYSECRETS" >&2
exit 1
fi
# Récupérer le chat_id sans l'afficher
RESPONSE=$(curl -s "https://api.telegram.org/bot${TOKEN}/getUpdates")
CHAT_ID=$(echo "$RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
results = data.get('result', [])
if not results:
print('NONE')
else:
print(results[-1].get('message', {}).get('chat', {}).get('id', 'NONE'))
" 2>/dev/null)
if [[ "$CHAT_ID" == "NONE" || -z "$CHAT_ID" ]]; then
echo "Aucun message reçu. Envoie /start au bot sur Telegram puis relance ce script." >&2
exit 1
fi
# Écrire dans MYSECRETS sans afficher la valeur
if grep -q '^BRAIN_TELEGRAM_CHAT_ID=' "$MYSECRETS"; then
sed -i "s/^BRAIN_TELEGRAM_CHAT_ID=.*/BRAIN_TELEGRAM_CHAT_ID=${CHAT_ID}/" "$MYSECRETS"
else
echo "BRAIN_TELEGRAM_CHAT_ID=${CHAT_ID}" >> "$MYSECRETS"
fi
echo "✅ BRAIN_TELEGRAM_CHAT_ID enregistré dans MYSECRETS — valeur non affichée"

119
scripts/install-brain-watch.sh Executable file
View File

@@ -0,0 +1,119 @@
#!/bin/bash
# install-brain-watch.sh — Installation du SUPERVISOR daemon
# Usage : bash install-brain-watch.sh [local|vps|both]
#
# Prérequis :
# - inotify-tools installé localement (sudo apt install inotify-tools)
# - MYSECRETS rempli (BRAIN_TELEGRAM_TOKEN + BRAIN_TELEGRAM_CHAT_ID)
# - Accès SSH root@VPS configuré
set -euo pipefail
TARGET="${1:-both}"
BRAIN_ROOT="${BRAIN_ROOT:-$HOME/Dev/Docs}"
VPS_USER="root"
VPS_IP=$(grep '^VPS_IP=' "$BRAIN_ROOT/MYSECRETS" | cut -d= -f2-)
VPS_WATCH_ROOT="/home/tetardtek/brain-watch"
GITEA_BRAIN_URL="git@git.tetardtek.com:Tetardtek/brain.git"
install_local() {
echo "=== Installation SUPERVISOR local ==="
chmod +x "$BRAIN_ROOT/scripts/brain-notify.sh"
chmod +x "$BRAIN_ROOT/scripts/brain-watch-local.sh"
# Lancer en background
LOGFILE="$HOME/brain-watch.log"
nohup "$BRAIN_ROOT/scripts/brain-watch-local.sh" >> "$LOGFILE" 2>&1 &
echo "PID $! — logs : $LOGFILE"
# Ajouter au .bashrc pour redémarrage automatique (si pas déjà présent)
MARKER="# brain-watch-local"
if ! grep -q "$MARKER" "$HOME/.bashrc" 2>/dev/null; then
cat >> "$HOME/.bashrc" << EOF
$MARKER
if ! pgrep -f "brain-watch-local.sh" > /dev/null; then
nohup $BRAIN_ROOT/scripts/brain-watch-local.sh >> $HOME/brain-watch.log 2>&1 &
fi
EOF
echo "Ajouté au .bashrc — démarrage automatique à l'ouverture du terminal"
fi
echo "✅ SUPERVISOR local installé"
}
install_vps() {
echo "=== Installation SUPERVISOR VPS ==="
SSH="ssh $VPS_USER@$VPS_IP"
# Créer le dossier
$SSH "mkdir -p $VPS_WATCH_ROOT"
# Copier les scripts
scp "$BRAIN_ROOT/scripts/brain-notify.sh" "$VPS_USER@$VPS_IP:$VPS_WATCH_ROOT/"
scp "$BRAIN_ROOT/scripts/brain-watch-vps.sh" "$VPS_USER@$VPS_IP:$VPS_WATCH_ROOT/"
$SSH "chmod +x $VPS_WATCH_ROOT/brain-notify.sh $VPS_WATCH_ROOT/brain-watch-vps.sh"
# Copier MYSECRETS (section brain-supervisor uniquement)
# On écrit un MYSECRETS minimal sur le VPS avec uniquement les clés Telegram
TOKEN=$(grep '^BRAIN_TELEGRAM_TOKEN=' "$BRAIN_ROOT/MYSECRETS" | cut -d= -f2-)
CHAT_ID=$(grep '^BRAIN_TELEGRAM_CHAT_ID=' "$BRAIN_ROOT/MYSECRETS" | cut -d= -f2-)
$SSH "cat > $VPS_WATCH_ROOT/MYSECRETS" << EOF
## brain-supervisor
BRAIN_TELEGRAM_TOKEN=$TOKEN
BRAIN_TELEGRAM_CHAT_ID=$CHAT_ID
EOF
$SSH "chmod 600 $VPS_WATCH_ROOT/MYSECRETS"
# Cloner le brain si pas déjà fait
$SSH "
if [[ ! -d $VPS_WATCH_ROOT/brain ]]; then
git clone $GITEA_BRAIN_URL $VPS_WATCH_ROOT/brain
echo 'Brain cloné'
else
echo 'Brain déjà cloné'
fi
"
# Installer le service systemd
$SSH "cat > /etc/systemd/system/brain-watch.service" << 'SYSTEMD'
[Unit]
Description=Brain SUPERVISOR — surveillance BRAIN-INDEX.md
After=network.target
[Service]
Type=simple
User=root
ExecStart=/home/tetardtek/brain-watch/brain-watch-vps.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
SYSTEMD
$SSH "systemctl daemon-reload && systemctl enable brain-watch && systemctl start brain-watch"
$SSH "systemctl status brain-watch --no-pager | head -10"
echo "✅ SUPERVISOR VPS installé (service brain-watch)"
}
case "$TARGET" in
local) install_local ;;
vps) install_vps ;;
both) install_local ; install_vps ;;
*) echo "Usage: $0 [local|vps|both]" ; exit 1 ;;
esac
echo ""
echo "=== Setup Telegram (si pas encore fait) ==="
echo "1. Ouvrir Telegram → chercher @BotFather → /newbot"
echo "2. Copier le token dans MYSECRETS : BRAIN_TELEGRAM_TOKEN=<token>"
echo "3. Envoyer /start au bot sur Telegram, puis :"
echo " bash brain/scripts/get-telegram-chatid.sh"
echo " → écrit BRAIN_TELEGRAM_CHAT_ID dans MYSECRETS directement — valeur jamais affichée"
echo "4. Tester : BRAIN_ROOT=~/Dev/Docs brain/scripts/brain-notify.sh 'Test SUPERVISOR' urgent"