5.0 KiB
#220 — Fusion : émission d'événements vers le journal du service d'agents
Contexte. Le Lot 2 du programme Fusion (carte fusion-trois-apps
#18) dote le service d'agents d'un journal d'activité cross-apps :
chaque application y émet ses mutations significatives au contrat
ACC-0.2 — enveloppe {id, type, occurred_at, actor, domain, source_app, object{ref, label, url}} signée HMAC-SHA256 du corps
brut (en-tête X-Draco-Signature, clé propre à chaque application),
que le service expose dans la vue Journal de sa console (agent-commun
0.11.0). TikTak (0.59.0) et Kanbante (0.28.0) émettent déjà ; ObsiGate
est la troisième source, après Flowdeck.
Principe directeur : référence, jamais contenu. L'enveloppe ne
contient que la référence de la note (fichier:<vault>/<chemin>),
son nom (libellé) et un lien profond (/#file=<vault>:<chemin>) —
jamais son contenu.
Garde-fou central : jamais le watcher
ObsiGate surveille les vaults par un watcher de système de
fichiers (backend/watcher.py) qui réindexe chaque changement
disque — le vault principal compte 222 393 fichiers. Brancher
l'émission sur le watcher inonderait le journal (et confondrait les
gestes d'Obsidian, de la synchro de fichiers ou d'un éditeur externe
avec des opérations faites dans ObsiGate).
L'émission n'a donc lieu que pour les opérations explicites via l'API web, à deux points d'accroche, et nulle part ailleurs :
| Source | Point d'accroche | Acteur |
|---|---|---|
| Interface web / clients REST | backend/routers/files_write.py |
user |
| Serveur MCP d'ObsiGate (outils) | backend/tools/service.py, mode mcp |
agent:mcp |
Assistant intégré (outils, mode in_app) |
backend/tools/service.py |
user |
Conséquences assumées :
- une note créée/modifiée hors API (Obsidian, éditeur externe, copie de fichiers) n'apparaît pas au journal — c'est voulu ;
- les écritures internes qui passent par la couche mutations sans passer par ces points (planificateur, crawler, fusion de doublons, restauration de sauvegarde) n'émettent pas non plus ;
- le test
test_watcher_et_indexation_sans_evenementverrouille le garde-fou : écriture disque directe + gestionnaires du watcher +update_single_filede l'indexeur → zéro émission.
Catalogue d'événements émis
| Type | Déclencheur |
|---|---|
obsigate.note.created |
POST /api/file/{vault} ; outil create_file |
obsigate.note.edited |
PUT /api/file/{vault}/save (+ variantes xlsx/csv : cellules, structure, style) ; outils edit_file, append_to_file |
obsigate.note.moved |
PATCH /api/file/{vault} (renommage), POST /api/move/{vault} (fichiers seulement) ; outils rename_file, move_path (fichiers seulement) — la référence pointe sur le nouveau chemin |
obsigate.note.deleted |
DELETE /api/file/{vault} ; outil delete_file |
Hors catalogue (non émis, volontairement) : dossiers (création,
renommage, déplacement, suppression — le catalogue est note.*),
téléversement par lots (batch-upload), duplication, restauration
de sauvegarde, replace_in_files (multi-fichiers).
Module backend/agent_journal.py
- Activation :
AGENT_EVENEMENTS_CLE(clé propre à ObsiGate, jamais en dur). Sans clé, le module est inactif : aucun appel, aucun bruit. URL surchargée parAGENT_EVENEMENTS_URL(défauthttps://agent.dracodev.net/api/v1/evenements). - Discipline « au mieux » (identique à TikTak/Kanbante) : envoi
dans un fil d'arrière-plan démon via httpx, délai maximal 2 s,
exceptions avalées avec une ligne de journal local — une panne du
service d'agents ne casse ni ne ralentit jamais le métier (prouvé
par
test_recepteur_en_panne_ne_casse_rien). - Domaine : ObsiGate ne modélise pas encore le domaine d'un
vault (la question Q-A du programme — les domaines sont-ils des
vaults distincts ? — est toujours ouverte). La correspondance peut
être déclarée par
AGENT_EVENEMENTS_DOMAINES(JSON{"NomDuVault": "famille"}) ; replipro, qui est aussi le défaut du journal du service.
Activation en production (gestes de Bruno)
- Générer une clé propre à ObsiGate (
openssl rand -hex 32). - Service d'agents : ajouter l'entrée
obsigateàAGENT_EVENEMENTS_CLES(JSON des clés par application), recréer le conteneur. - ObsiGate : poser
AGENT_EVENEMENTS_CLE=<même valeur>dans son environnement et redéployer (Docker).
Tant que la clé n'est pas posée des deux côtés, ObsiGate n'émet rien — c'est le comportement voulu.
Tests
tests/test_agent_journal.py (7 tests) : enveloppe et signature
revérifiées comme le fait le service ; cycle de vie complet en REST
(créer → éditer → renommer → déplacer → supprimer, acteur user) ;
chemin outils/MCP (create/edit/rename/delete, acteur
agent:mcp) ; inactivité sans clé ; garde-fou watcher/indexation ;
panne du récepteur sans effet métier ; correspondance de domaines.