labfy-investigation/docs/architecture/EML_ANALYSIS_TO_GRAPH.md
fy59 d65f9559e6 chore(checkpoint): preserve local investigation workbench
Preserve the local graph-first investigation stack through macro V1.

Include reviewed source, tests, contracts, documentation and synthetic assets.

Retain the standalone financial V21 test foundation without runtime migration.

Runtime remains V20; operational JobStore remains V3.

Local experimental checkpoint, not a stable release.
2026-09-27 08:34:46 +02:00

5.8 KiB

Analyse EML persistée vers le graphe Web — lot J3

État : CURRENT local, synthétique, non publié Runtime d'écriture et de lecture : V20 inchangé Contrat transport : labfy.web_graph.snapshot.v3

Ce lot exécute réellement l'analyseur natif eml_analyzer sur une preuve EML importée par les API de production. Les observations ne sont ni préinsérées ni reconstruites par Python ou JavaScript : le service C les calcule, les publie dans SQLite avec leur extraction, ferme la connexion d'écriture, puis la projection C les relit en lecture seule pour produire le snapshot Web.

EML SPECIMEN
  → EvidenceImporter → preuve originale contrôlée
  → EmlAnalysisPersistenceService::prepare
       chemin relatif + anti-symlink + taille + SHA-256
       eml_analyzer_analyze_file (en-têtes uniquement)
       dérivé JSON versionné en mémoire
  → staging privé + rename du dérivé
  → transaction SQLite unique
       preuve dérivée + extraction + observations proposed
  → fermeture / réouverture V20 read-only
  → CoreGraphProjectionService → snapshot v3
  → serveur statique loopback → graphe Web

Contrat du service

La demande fournit trois UUID distincts et une date UTC : preuve source, identité de requête/extraction et identité de preuve dérivée. Le service emprunte une Database ouverte et copie la racine d'enquête ; l'appelant reste propriétaire des deux. prepare() possède son résultat jusqu'à eml_analysis_prepared_free() et ne modifie ni SQLite ni le disque. publish() retourne un résultat possédé, libéré avec eml_analysis_publication_result_free().

Avant l'analyse comme avant un replay idempotent, la copie contrôlée est un fichier régulier sous la racine, sans composant symbolique. Sa taille et son SHA-256 doivent être identiques au EvidenceRecord. Les limites locales sont 4 Mio pour la source, 256 observations et 1 Mio pour le dérivé. Elles bornent le travail de cette capacité sans imposer une limite scientifique globale à l'enquête.

Le dérivé labfy.eml_analysis.derivative.v1 conserve :

  • UUID et date de demande ;
  • UUID, taille et SHA-256 de l'original ;
  • labfy.eml_analyzer, version 1 ;
  • portée headers-only et limites appliquées ;
  • pour chaque observation : type, brut, normalisé, rôle, en-tête, occurrence et nature de provenance.

Le dérivé est préparé hors transaction, écrit dans un staging privé puis renommé. La preuve dérivée, l'extraction et toutes les observations sont ensuite insérées dans une transaction SQLite unique. Un échec annule les lignes et supprime uniquement le fichier final créé par cette tentative. Les originaux ne sont jamais modifiés.

Idempotence et réanalyse

L'UUID de demande est la clé idempotente. Un replay après réouverture vérifie à nouveau l'original, le dérivé, son hash, son contrat, l'outil, les paramètres, la date et le lot exact d'observations persistées. Il retourne le même résultat sans seconde publication. Une identité réutilisée avec une source, un dérivé, une date ou des observations incompatibles produit un conflit explicite. Un dérivé orphelin ou un lot incomplet demande une récupération explicite.

Une réanalyse est une nouvelle demande, avec un nouvel UUID d'extraction et un nouvel UUID de dérivé. Elle peut donc produire un second lot proposé sans écraser le premier. Le scénario de démonstration exerce les deux cas : replay de la première demande, puis réanalyse distincte.

Persistance V20 et projection v3

Aucune migration ni DDL n'est ajouté. Le lot réutilise :

Objet Persistance V20 Relation
original EML preuves entrée de l'analyse
dérivé structuré preuves extractions.evidence_id
tentative publiée extractions source_kind=evidence, source_id=<original>
observation calculée evidence_entity_observations evidence_id=<original>, extraction_id=<demande>

Les observations restent proposed, non promues et sans entité créée. Le lot ne fabrique pas d'osint_execution : le runtime V20 ne permet pas d'y exprimer une sélection de type preuve, et falsifier cette exécution détruirait la provenance.

Le snapshot v2 demeure inchangé pour le scénario J3 read-only historique. La v3 ajoute seulement object_kind: extraction, les nœuds d'extraction et les arêtes de provenance analysis_input, analysis_derivative et analysis_observation. L'original, le dérivé et chaque observation conservent leurs identités SQLite. Le navigateur reçoit ce snapshot ; il ne lit jamais la base ou le dérivé et n'invente aucune observation.

Démonstration et validation

make -j8 eml-graph-demo
cd prototypes/web-graph
python3 run_eml_demo.py --port 8765

Le lanceur crée une racine privée temporaire, exécute un binaire fixé avec des arguments séparés et un délai de 45 secondes, puis ne sert que le snapshot accompagné du manifeste EML attendu. --variant alternate produit un autre EML synthétique afin de prouver que l'affichage suit le contenu réellement analysé. Un échec C est affiché ; aucun repli vers les fixtures J2 n'est autorisé.

Les tests C couvrent analyse, valeurs brutes/normalisées, provenance, fermeture/réouverture, replay, conflit, altération, source absente, limite, analyse vide, annulation, liens symboliques, rollback et intégrité SQLite. Les tests Firefox couvrent snapshot v3, deux extractions, deux dérivés, observations proposées, navigation de provenance, recherche, focus/retour, drag, vue étroite, erreur sans repli et contenu HTML inerte. Toutes les données utilisent .test, les plages IP de documentation et des noms SPECIMEN.

Ce lot ne livre ni analyse automatique d'une enquête ouverte, ni promotion d'entité, ni mutation Web, ni serveur de production, ni action réseau OSINT.

J3_EML_ANALYSIS_TO_GRAPH = COMPLETE_LOCAL