labfy-investigation/docs/database/SCHEMA_AUDIT_V1.md
2026-07-15 23:10:24 +02:00

44 KiB

Audit du schéma SQLite V1

Version : 1.0

Date : 2026-07-15


Objectif

Ce document analyse le schéma historique de Labfy Investigation afin de définir le schéma métier officiel V1.

Chaque table historique reçoit l'une des décisions suivantes :

  • CONSERVER
  • MODIFIER
  • RENOMMER
  • FUSIONNER
  • SUPPRIMER

Le schéma historique sert de fondation. Les concepts utiles sont conservés, mais les noms, contraintes et relations peuvent être corrigés.


Conventions générales

Identifiants métier

Tous les objets métier utilisent un UUID stocké sous forme de texte :

id TEXT PRIMARY KEY

L'UUID est généré par l'application avec :

g_uuid_string_random()

Les tables de référence utilisent des identifiants entiers.


Dates

Les dates techniques sont stockées en UTC au format ISO 8601 :

YYYY-MM-DDTHH:MM:SSZ

Chemins

Tous les chemins enregistrés dans la base sont relatifs à la racine de l'enquête.

Exemple valide :

01_Preuves_Originales/Documents/facture.pdf

Exemple interdit :

/home/utilisateur/Enquetes/Test/01_Preuves_Originales/Documents/facture.pdf

Nommage SQL

Les noms utilisent :

  • des minuscules ;
  • le snake_case ;
  • aucun accent ;
  • aucun espace.

Suppression logique

Les objets importants ne sont pas supprimés immédiatement de la base.

Un statut métier permet de conserver leur historique et leurs relations.

Une purge physique éventuelle devra être une opération explicite et journalisée.


Audit des tables

Table historique preuves

Décision

MODIFIER

Nom final

preuves

Justification

Le concept est central et doit être conservé.

La table historique doit cependant être renforcée afin de garantir :

  • l'identification unique des preuves ;
  • la portabilité des chemins ;
  • l'intégrité des fichiers ;
  • la traçabilité de l'import ;
  • la suppression logique ;
  • la cohérence des types et statuts.

Définition métier

Une preuve représente un élément collecté dans le cadre d'une enquête.

Elle peut correspondre notamment à :

  • une capture d'écran ;
  • une photographie ;
  • une vidéo ;
  • un document ;
  • un email exporté ;
  • une archive ;
  • un fichier audio ;
  • un journal technique ;
  • un résultat produit par un outil.

Le contenu d'une preuve originale ne doit jamais être modifié.

Les annotations, conversions, extractions et autres transformations produisent de nouveaux fichiers dans 02_Preuves_Traitees.


Identifiant

La colonne :

id

contient directement l'UUID de la preuve.

Il n'est pas nécessaire d'avoir deux colonnes distinctes id et uuid.

Exemple :

550e8400-e29b-41d4-a716-446655440000

Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de la preuve
name TEXT oui Nom visible du fichier ou de l'élément
relative_path TEXT oui Chemin relatif dans l'enquête
type_id INTEGER oui Référence vers types_preuve
source_id TEXT non Référence vers la source principale
size_bytes INTEGER non Taille du fichier en octets
sha256 TEXT non Empreinte SHA-256
mime_type TEXT non Type MIME détecté
description TEXT non Description synthétique
comment TEXT non Notes détaillées
file_created_at TEXT non Date de création connue du fichier
imported_at TEXT oui Date d'ajout dans l'enquête
updated_at TEXT oui Dernière modification des métadonnées
status TEXT oui État logique de la preuve
locked INTEGER oui Verrouillage logique, 0 ou 1

Statuts autorisés

Première version :

active
archived
deleted

Une preuve marquée :

deleted

reste présente dans la base afin de préserver :

  • le journal ;
  • les relations ;
  • les références dans les rapports ;
  • la traçabilité.

Contraintes prévues

PRIMARY KEY (id)
UNIQUE (relative_path)
FOREIGN KEY (type_id)
    REFERENCES types_preuve(id)
    ON UPDATE CASCADE
    ON DELETE RESTRICT
FOREIGN KEY (source_id)
    REFERENCES sources(id)
    ON UPDATE CASCADE
    ON DELETE SET NULL
CHECK (length(trim(name)) > 0)
CHECK (length(trim(relative_path)) > 0)
CHECK (size_bytes IS NULL OR size_bytes >= 0)
CHECK (
    sha256 IS NULL
    OR (
        length(sha256) = 64
        AND sha256 = lower(sha256)
    )
)
CHECK (status IN ('active', 'archived', 'deleted'))
CHECK (locked IN (0, 1))

Index prévus

La contrainte UNIQUE crée déjà un index sur :

relative_path

Ajouter également :

CREATE INDEX idx_preuves_type_id
ON preuves(type_id);
CREATE INDEX idx_preuves_source_id
ON preuves(source_id);
CREATE INDEX idx_preuves_status
ON preuves(status);
CREATE INDEX idx_preuves_sha256
ON preuves(sha256);

L'index sur sha256 permettra notamment de détecter rapidement les fichiers déjà présents dans l'enquête.


Relation avec les sources

Une preuve peut posséder une source principale via :

source_id

Ce champ ne remplace pas nécessairement toutes les relations possibles.

Une future table de liaison pourra être ajoutée si une preuve doit être reliée à plusieurs sources :

preuve_sources

Pour le schéma V1, une source principale est suffisante tant que le besoin multi-source n'est pas confirmé.


Relation avec les tags

La relation entre les preuves et les tags sera stockée dans :

preuve_tags

La table historique :

preuves_tag

sera donc renommée et consolidée.


Structure C cible

La structure C envisagée est :

typedef struct InvestigationEvidence InvestigationEvidence;

Sa représentation privée contiendra conceptuellement :

struct InvestigationEvidence
{
    char *id;
    char *name;
    char *relative_path;

    int type_id;
    char *source_id;

    guint64 size_bytes;

    char *sha256;
    char *mime_type;
    char *description;
    char *comment;

    char *file_created_at;
    char *imported_at;
    char *updated_at;

    char *status;
    gboolean locked;
};

Cette structure n'est pas implémentée dans le ticket #023.

Elle sert uniquement à vérifier que le schéma SQL correspond bien au futur modèle C.


Modifications par rapport au schéma historique

  • conservation du concept preuves ;
  • adoption d'un UUID comme clé primaire ;
  • utilisation exclusive d'un chemin relatif ;
  • ajout de contraintes NOT NULL ;
  • ajout de la taille en octets ;
  • ajout du hash SHA-256 ;
  • ajout du type MIME ;
  • séparation entre date du fichier et date d'import ;
  • ajout d'une date de mise à jour ;
  • ajout d'un statut de suppression logique ;
  • ajout du verrouillage ;
  • ajout d'une relation optionnelle vers sources ;
  • ajout d'index métier ;
  • renommage futur de preuves_tag en preuve_tags.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `preuves`

Table historique sources

Décision

MODIFIER

Nom final

sources

Justification

Le concept doit être conservé.

Une source représente l'origine d'une information ou d'une preuve. Elle ne représente pas la preuve elle-même.

Exemples :

  • site web ;
  • réseau social ;
  • témoin ;
  • téléphone ;
  • disque ;
  • document ;
  • email ;
  • outil externe.

La table historique doit être renforcée avec :

  • un UUID ;
  • un type obligatoire ;
  • une référence technique générique ;
  • des dates techniques ;
  • des contraintes explicites.

Colonnes finales proposées

Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de la source
type_id INTEGER oui Référence vers types_source
nom TEXT oui Nom visible de la source
reference TEXT non URL, identifiant, numéro de série, Message-ID ou autre référence technique
description TEXT non Description libre
created_at TEXT oui Date de création dans l'enquête
updated_at TEXT oui Dernière modification des métadonnées

Contraintes prévues

PRIMARY KEY (id)
FOREIGN KEY (type_id)
    REFERENCES types_source(id)
    ON UPDATE CASCADE
    ON DELETE RESTRICT
CHECK (length(trim(nom)) > 0)
CHECK (
    reference IS NULL
    OR length(trim(reference)) > 0
)

Relation avec les preuves

Le modèle prévu est :

source
    ↓
recherche
    ↓
preuve

Une recherche documentera :

  • la source interrogée ;
  • l'outil utilisé ;
  • la requête ;
  • les résultats ;
  • les preuves produites.

Index prévus

CREATE INDEX idx_sources_type_id
ON sources(type_id);
CREATE INDEX idx_sources_reference
ON sources(reference);

Structure C cible

typedef struct Source Source;
struct Source
{
    char *id;

    int type_id;

    char *nom;
    char *reference;
    char *description;

    char *created_at;
    char *updated_at;
};

Cette structure n'est pas implémentée dans le ticket #023.


Modifications par rapport au schéma historique

  • conservation du concept sources ;
  • UUID comme clé primaire ;
  • type obligatoire ;
  • remplacement des champs trop spécialisés par reference ;
  • ajout des dates techniques ;
  • ajout des contraintes ;
  • ajout des index ;
  • absence de relation directe avec preuves.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `sources`

Table historique recherche

Décision

RENOMMER ET MODIFIER

Nom final

recherches

Justification

Le concept doit être conservé, mais la table est renommée au pluriel afin de respecter les conventions SQL du projet.

Une recherche représente une opération documentée réalisée au cours d'une enquête.

Elle peut correspondre à :

  • une requête sur un moteur de recherche ;
  • une consultation de profil social ;
  • une requête DNS ou WHOIS ;
  • une analyse de fichier ;
  • une extraction de métadonnées ;
  • une recherche dans une base publique ;
  • une procédure manuelle ;
  • l'exécution d'un script ou d'un outil OSINT.

La recherche conserve le contexte de l'opération afin que son résultat puisse être compris et reproduit.


Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de la recherche
source_id TEXT non Source principalement interrogée
type_outil_id INTEGER oui Catégorie de l'outil utilisé
outil_nom TEXT oui Nom précis de l'outil ou de la procédure
requete TEXT non Requête, commande ou opération exécutée
resultat TEXT non Résultat synthétique
observations TEXT non Notes et interprétations
started_at TEXT oui Début de l'opération en UTC
completed_at TEXT non Fin de l'opération en UTC
created_at TEXT oui Création de l'enregistrement
updated_at TEXT oui Dernière modification
status TEXT oui État logique de la recherche

Statuts autorisés

planned
running
completed
failed
cancelled
archived

Une recherche échouée reste enregistrée.

Un échec peut être important pour comprendre :

  • les méthodes déjà essayées ;
  • les limitations rencontrées ;
  • les changements d'une cible ;
  • les erreurs techniques ;
  • les étapes non reproductibles.

Relation avec les sources

La colonne :

source_id

est facultative.

Certaines recherches interrogent une source identifiable :

profil Instagram
site web
export téléphonique
document

D'autres opérations n'en possèdent pas directement :

calcul de hash
OCR local
analyse de métadonnées
script interne

La suppression logique ou physique d'une source ne doit pas supprimer l'historique des recherches.

La relation utilisera donc :

ON DELETE SET NULL

Relation avec les outils

types_outil représente une catégorie technique :

dns_tool
whois_tool
web_browser
ocr_tool
custom_script

La colonne :

outil_nom

contient le nom concret utilisé :

dig
Firefox
ExifTool
Tesseract
labfy-whois

Cela évite de créer immédiatement une table outils alors que le besoin d'un catalogue complet d'outils n'est pas encore établi.


Relation avec les preuves

Une recherche peut :

  • utiliser une preuve comme donnée d'entrée ;
  • produire une preuve comme résultat.

Une preuve peut également être liée à plusieurs recherches.

La relation est donc plusieurs-à-plusieurs et sera stockée dans :

recherche_preuves

Cette table comportera un rôle :

input
output

Contraintes prévues

PRIMARY KEY (id)
FOREIGN KEY (source_id)
    REFERENCES sources(id)
    ON UPDATE CASCADE
    ON DELETE SET NULL
FOREIGN KEY (type_outil_id)
    REFERENCES types_outil(id)
    ON UPDATE CASCADE
    ON DELETE RESTRICT
CHECK (length(trim(outil_nom)) > 0)
CHECK (
    requete IS NULL
    OR length(trim(requete)) > 0
)
CHECK (
    completed_at IS NULL
    OR completed_at >= started_at
)
CHECK (
    status IN (
        'planned',
        'running',
        'completed',
        'failed',
        'cancelled',
        'archived'
    )
)

Index prévus

CREATE INDEX idx_recherches_source_id
ON recherches(source_id);
CREATE INDEX idx_recherches_type_outil_id
ON recherches(type_outil_id);
CREATE INDEX idx_recherches_status
ON recherches(status);
CREATE INDEX idx_recherches_started_at
ON recherches(started_at);

Structure C cible

typedef struct Recherche Recherche;

Représentation privée envisagée :

struct Recherche
{
    char *id;
    char *source_id;

    int type_outil_id;
    char *outil_nom;

    char *requete;
    char *resultat;
    char *observations;

    char *started_at;
    char *completed_at;
    char *created_at;
    char *updated_at;

    char *status;
};

Cette structure ne sera pas implémentée dans le ticket #023.


Modifications par rapport au schéma historique

  • renommage de recherche en recherches ;
  • adoption d'un UUID comme clé primaire ;
  • ajout d'une source optionnelle ;
  • distinction entre catégorie et nom concret de l'outil ;
  • ajout du contexte de la requête ;
  • ajout des résultats et observations ;
  • ajout des dates de début et de fin ;
  • ajout d'un statut ;
  • création d'une relation plusieurs-à-plusieurs avec les preuves.

Décision finale

CONSERVER LE CONCEPT
RENOMMER EN `recherches`
MODIFIER LE SCHÉMA

Table historique entites

Décision

MODIFIER

Nom final

entites

Justification

Le concept est central et doit être conservé.

Une entité représente une information identifiable découverte au cours de l'enquête.

Elle peut être :

  • extraite d'une preuve ;
  • produite par une recherche ;
  • ajoutée manuellement ;
  • enrichie progressivement ;
  • reliée à d'autres entités.

La table historique doit être renforcée avec :

  • un UUID ;
  • un type obligatoire ;
  • une valeur canonique ;
  • une valeur d'affichage ;
  • un statut ;
  • des dates techniques ;
  • des contraintes explicites.

Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de l'entité
type_id INTEGER oui Référence vers types_entite
valeur TEXT oui Valeur canonique utilisée pour les comparaisons
label TEXT non Valeur lisible ou nom d'affichage
description TEXT non Description libre
confiance INTEGER oui Niveau de confiance de 0 à 100
created_at TEXT oui Date de création dans l'enquête
updated_at TEXT oui Dernière modification
status TEXT oui État logique de l'entité

Différence entre valeur et label

La colonne :

valeur

contient la forme canonique utilisée pour les recherches et les doublons.

Exemples :

contact@example.com
+33612345678
arnaque_123
example.com
192.0.2.10

La colonne :

label

contient une forme d'affichage facultative.

Exemples :

Adresse principale
Téléphone du vendeur
Compte Instagram suspect
Site de la société

Niveau de confiance

La colonne :

confiance

utilise une valeur entière comprise entre :

0 et 100

Exemples :

100 = vérifié
80  = fortement probable
50  = à confirmer
20  = faible
0   = inconnu

La valeur par défaut est :

50

Statuts autorisés

active
archived
deleted

Une entité supprimée logiquement reste dans la base afin de préserver :

  • les recherches ;
  • les relations ;
  • les rapports ;
  • la chronologie ;
  • le journal.

Contraintes prévues

PRIMARY KEY (id)
FOREIGN KEY (type_id)
    REFERENCES types_entite(id)
    ON UPDATE CASCADE
    ON DELETE RESTRICT
CHECK (length(trim(valeur)) > 0)
CHECK (
    label IS NULL
    OR length(trim(label)) > 0
)
CHECK (confiance BETWEEN 0 AND 100)
CHECK (
    status IN (
        'active',
        'archived',
        'deleted'
    )
)

Unicité

Une même valeur peut exister pour plusieurs types différents.

Exemple :

valeur = "example.com"

pourrait être interprétée comme :

  • nom de domaine ;
  • site web.

L'unicité doit donc porter sur :

UNIQUE (type_id, valeur)

Index prévus

CREATE INDEX idx_entites_type_id
ON entites(type_id);
CREATE INDEX idx_entites_valeur
ON entites(valeur);
CREATE INDEX idx_entites_status
ON entites(status);
CREATE INDEX idx_entites_confiance
ON entites(confiance);

Relation avec les recherches

Une recherche peut découvrir ou enrichir plusieurs entités.

Une entité peut être liée à plusieurs recherches.

La relation sera stockée dans :

recherche_entites

avec un rôle :

discovered
enriched
validated
contradicted

Relation avec les preuves

Une entité peut être extraite d'une ou plusieurs preuves.

Une preuve peut contenir plusieurs entités.

Cette relation sera stockée dans :

preuve_entites

Structure C cible

typedef struct Entite Entite;

Représentation privée envisagée :

struct Entite
{
    char *id;

    int type_id;

    char *valeur;
    char *label;
    char *description;

    int confiance;

    char *created_at;
    char *updated_at;

    char *status;
};

Cette structure n'est pas implémentée dans le ticket #023.


Modifications par rapport au schéma historique

  • conservation du concept entites ;
  • UUID comme clé primaire ;
  • type obligatoire ;
  • séparation entre valeur canonique et label d'affichage ;
  • ajout d'un niveau de confiance ;
  • ajout d'un statut logique ;
  • ajout des dates techniques ;
  • ajout d'une contrainte d'unicité ;
  • ajout de relations avec les recherches et les preuves.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `entites`

Table historique associations

Décision

RENOMMER ET MODIFIER

Nom final

relations

Justification

Le concept de liaison entre objets métier doit être conservé, mais la table historique associations est trop générique.

Une relation représente un lien documenté entre deux entités.

Exemples :

  • une personne utilise une adresse email ;
  • un pseudonyme appartient probablement à une personne ;
  • un compte social utilise un numéro de téléphone ;
  • un IBAN est associé à une adresse email ;
  • un domaine pointe vers une adresse IP ;
  • une organisation possède un site web.

Une relation n'est pas un simple raccord technique. Elle constitue une information d'enquête à part entière.

Elle doit donc posséder :

  • un UUID ;
  • une entité source ;
  • une entité cible ;
  • un type de relation ;
  • un niveau de confiance ;
  • une justification ;
  • des dates ;
  • un statut.

Nom et orientation

Une relation est orientée :

entite_source
    │
    ▼
entite_cible

Exemple :

Personne
    │ utilise
    ▼
Adresse email

L'orientation doit être choisie de manière cohérente lors de la création.

La relation inverse ne doit pas être créée automatiquement dans la base.


Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de la relation
entite_source_id TEXT oui Entité à l'origine de la relation
entite_cible_id TEXT oui Entité ciblée par la relation
type_relation TEXT oui Code stable décrivant la relation
label TEXT non Libellé d'affichage facultatif
justification TEXT non Explication du raisonnement
confiance INTEGER oui Niveau de confiance de 0 à 100
created_at TEXT oui Date de création
updated_at TEXT oui Dernière modification
status TEXT oui État logique de la relation

Type de relation

La colonne :

type_relation

contient un code technique stable.

Exemples :

uses
owns
belongs_to
linked_to
communicates_with
resolves_to
registered_with
paid_to
same_as
possibly_same_as

Le libellé humain pourra être traduit plus tard.

Pour la V1, aucune table de référence types_relation n'est imposée.

Le type reste une chaîne contrôlée par l'application.

Une table dédiée pourra être introduite si le catalogue devient important.


Niveau de confiance

La colonne :

confiance

est comprise entre :

0 et 100

Exemples :

100 = relation vérifiée
80  = fortement probable
50  = hypothèse raisonnable
20  = faible indice
0   = inconnu

Statuts autorisés

active
archived
deleted
disputed

Le statut :

disputed

indique que la relation est contestée ou qu'un élément contradictoire a été découvert.

La relation reste conservée pour préserver l'historique du raisonnement.


Contraintes prévues

PRIMARY KEY (id)
FOREIGN KEY (entite_source_id)
    REFERENCES entites(id)
    ON UPDATE CASCADE
    ON DELETE RESTRICT
FOREIGN KEY (entite_cible_id)
    REFERENCES entites(id)
    ON UPDATE CASCADE
    ON DELETE RESTRICT
CHECK (entite_source_id <> entite_cible_id)
CHECK (length(trim(type_relation)) > 0)
CHECK (
    label IS NULL
    OR length(trim(label)) > 0
)
CHECK (confiance BETWEEN 0 AND 100)
CHECK (
    status IN (
        'active',
        'archived',
        'deleted',
        'disputed'
    )
)

Unicité

Un même couple d'entités peut posséder plusieurs types de relations.

Exemple :

Personne A
    owns
Compte X

Personne A
    uses
Compte X

L'unicité doit donc porter sur :

UNIQUE (
    entite_source_id,
    entite_cible_id,
    type_relation
)

Cela empêche un doublon exact sans interdire plusieurs relations distinctes.


Relation avec les preuves

Une relation peut être soutenue par plusieurs preuves.

Une preuve peut soutenir plusieurs relations.

La liaison sera stockée dans :

relation_preuves

Relation avec les recherches

Une relation peut être découverte, confirmée ou contredite par plusieurs recherches.

La liaison sera stockée dans :

recherche_relations

avec un rôle :

discovered
confirmed
contradicted

Index prévus

CREATE INDEX idx_relations_entite_source_id
ON relations(entite_source_id);
CREATE INDEX idx_relations_entite_cible_id
ON relations(entite_cible_id);
CREATE INDEX idx_relations_type_relation
ON relations(type_relation);
CREATE INDEX idx_relations_status
ON relations(status);
CREATE INDEX idx_relations_confiance
ON relations(confiance);

Structure C cible

typedef struct Relation Relation;

Représentation privée envisagée :

struct Relation
{
    char *id;

    char *entite_source_id;
    char *entite_cible_id;

    char *type_relation;
    char *label;
    char *justification;

    int confiance;

    char *created_at;
    char *updated_at;

    char *status;
};

Cette structure n'est pas implémentée dans le ticket #023.


Modifications par rapport au schéma historique

  • remplacement de associations par relations ;
  • UUID comme clé primaire ;
  • relation orientée entre deux entités ;
  • ajout d'un type de relation ;
  • ajout d'un niveau de confiance ;
  • ajout d'une justification ;
  • ajout d'un statut contestable ;
  • ajout des dates techniques ;
  • ajout de liaisons avec les preuves et les recherches ;
  • ajout de contraintes et d'index.

Décision finale

CONSERVER LE CONCEPT
RENOMMER `associations` EN `relations`
MODIFIER PROFONDÉMENT LE SCHÉMA

Table historique chronologie

Décision

MODIFIER

Nom final

chronologie

Justification

Le concept doit être conservé.

Une entrée de chronologie représente un événement important de l'enquête.

Elle peut être :

  • saisie manuellement ;
  • produite par une recherche ;
  • associée à une preuve ;
  • liée à une entité ;
  • liée à une relation ;
  • générée automatiquement par l'application.

La chronologie ne doit pas être confondue avec le journal technique.

La chronologie raconte les événements utiles à la compréhension de l'enquête.

Le journal enregistre les actions réalisées dans le logiciel.


Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de l'événement
event_time TEXT oui Date et heure de l'événement décrit
titre TEXT oui Titre court
description TEXT non Description détaillée
origine TEXT oui Origine manuelle ou automatique
created_at TEXT oui Date de création de l'enregistrement
updated_at TEXT oui Dernière modification
status TEXT oui État logique

Origines autorisées

manual
automatic
imported

manual :

  • saisie directe par l'enquêteur.

automatic :

  • génération par Labfy Investigation.

imported :

  • événement importé depuis une source externe.

Statuts autorisés

active
archived
deleted

Une entrée supprimée logiquement reste conservée afin de préserver :

  • les rapports ;
  • les références ;
  • les liens avec les preuves ;
  • l'historique de l'enquête.

Relation avec les objets métier

Une entrée de chronologie peut être liée à plusieurs objets métier.

Les relations sont stockées dans des tables spécialisées :

recherche_chronologie
preuve_chronologie
entite_chronologie
relation_chronologie

Cette solution est préférée à un couple polymorphe :

objet_type
objet_id

car elle permet de conserver de vraies clés étrangères SQLite.


Contraintes prévues

PRIMARY KEY (id)
CHECK (length(trim(titre)) > 0)
CHECK (
    description IS NULL
    OR length(trim(description)) > 0
)
CHECK (
    origine IN (
        'manual',
        'automatic',
        'imported'
    )
)
CHECK (
    status IN (
        'active',
        'archived',
        'deleted'
    )
)

Index prévus

CREATE INDEX idx_chronologie_event_time
ON chronologie(event_time);
CREATE INDEX idx_chronologie_origine
ON chronologie(origine);
CREATE INDEX idx_chronologie_status
ON chronologie(status);

Structure C cible

typedef struct EvenementChronologie EvenementChronologie;

Représentation privée envisagée :

struct EvenementChronologie
{
    char *id;

    char *event_time;

    char *titre;
    char *description;

    char *origine;

    char *created_at;
    char *updated_at;

    char *status;
};

Cette structure n'est pas implémentée dans le ticket #023.


Modifications par rapport au schéma historique

  • conservation du concept chronologie ;
  • UUID comme clé primaire ;
  • séparation entre date de l'événement et dates techniques ;
  • ajout d'un titre obligatoire ;
  • ajout d'une origine ;
  • ajout d'un statut logique ;
  • ajout de relations explicites vers les objets métier ;
  • ajout de contraintes et d'index.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `chronologie`

Table historique journal

Décision

MODIFIER

Nom final

journal

Justification

Le concept doit être conservé.

Le journal constitue la trace d'audit des actions significatives réalisées dans Labfy Investigation.

Il ne décrit pas directement les événements de l'enquête.

Il décrit les opérations effectuées dans le logiciel.

Exemples :

  • création d'une enquête ;
  • import d'une preuve ;
  • modification d'une entité ;
  • création d'une relation ;
  • changement du niveau de confiance ;
  • archivage d'un objet ;
  • génération d'un rapport ;
  • calcul ou vérification d'un hash.

Le journal doit permettre de répondre aux questions suivantes :

  • quelle action a été effectuée ;
  • quand elle a été effectuée ;
  • sur quel objet ;
  • avec quel résultat ;
  • avec quels détails complémentaires.

Différence avec chronologie

La table :

chronologie

raconte les événements utiles à la compréhension de l'enquête.

La table :

journal

raconte les actions effectuées dans l'application.

Exemple :

Chronologie :
Le compte Instagram suspect a été découvert à 14 h 32.

Journal :
Une entité de type compte Instagram a été créée à 14 h 34.

Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de l'entrée du journal
event_time TEXT oui Date et heure de l'action
action TEXT oui Code technique de l'action
objet_type TEXT non Type de l'objet concerné
objet_id TEXT non UUID de l'objet concerné
resultat TEXT oui Résultat de l'action
details TEXT non Informations complémentaires
acteur TEXT non Utilisateur, processus ou composant ayant réalisé l'action
created_at TEXT oui Date de création de l'enregistrement

Actions prévues

Les actions utilisent un code technique stable.

Exemples :

create
update
archive
restore
delete
import
export
open
close
hash_compute
hash_verify
report_generate
relation_create
relation_dispute

La liste n'est pas limitée par une contrainte SQL afin de permettre l'évolution du logiciel sans migration du schéma.

L'application reste responsable de l'utilisation de codes cohérents.


Types d'objets possibles

Exemples :

investigation
preuve
source
recherche
entite
relation
chronologie
hypothese
rapport

Une action peut ne concerner aucun objet précis.

Exemple :

application_start
application_stop

Dans ce cas :

objet_type = NULL
objet_id   = NULL

Cohérence entre objet_type et objet_id

Les deux colonnes doivent être :

  • toutes les deux nulles ;
  • ou toutes les deux renseignées.

Contrainte prévue :

CHECK (
    (
        objet_type IS NULL
        AND objet_id IS NULL
    )
    OR
    (
        objet_type IS NOT NULL
        AND objet_id IS NOT NULL
    )
)

Le journal utilise volontairement une relation polymorphe.

Aucune clé étrangère ne peut garantir automatiquement l'existence de l'objet référencé.

L'application doit vérifier la cohérence lors de l'écriture.


Résultats autorisés

success
failure
partial
cancelled

Une opération échouée doit rester enregistrée.


Contraintes prévues

PRIMARY KEY (id)
CHECK (length(trim(action)) > 0)
CHECK (
    objet_type IS NULL
    OR length(trim(objet_type)) > 0
)
CHECK (
    objet_id IS NULL
    OR length(trim(objet_id)) > 0
)
CHECK (
    (
        objet_type IS NULL
        AND objet_id IS NULL
    )
    OR
    (
        objet_type IS NOT NULL
        AND objet_id IS NOT NULL
    )
)
CHECK (
    resultat IN (
        'success',
        'failure',
        'partial',
        'cancelled'
    )
)
CHECK (
    details IS NULL
    OR length(trim(details)) > 0
)
CHECK (
    acteur IS NULL
    OR length(trim(acteur)) > 0
)

Index prévus

CREATE INDEX idx_journal_event_time
ON journal(event_time);
CREATE INDEX idx_journal_action
ON journal(action);
CREATE INDEX idx_journal_objet
ON journal(objet_type, objet_id);
CREATE INDEX idx_journal_resultat
ON journal(resultat);

Structure C cible

typedef struct EntreeJournal EntreeJournal;

Représentation privée envisagée :

struct EntreeJournal
{
    char *id;

    char *event_time;
    char *action;

    char *objet_type;
    char *objet_id;

    char *resultat;
    char *details;
    char *acteur;

    char *created_at;
};

Cette structure n'est pas implémentée dans le ticket #023.


Suppression

Les entrées du journal ne doivent pas être modifiées ni supprimées par les fonctions métier ordinaires.

Une éventuelle purge devra être :

  • explicite ;
  • réservée à une opération administrative ;
  • documentée ;
  • elle-même journalisée lorsque cela est possible.

Modifications par rapport au schéma historique

  • conservation du concept journal ;
  • UUID comme clé primaire ;
  • séparation entre action, objet, résultat et détails ;
  • ajout de l'acteur ;
  • ajout de contraintes de cohérence ;
  • ajout d'index ;
  • affirmation du caractère append-only du journal.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `journal`

Table historique hypotheses

Décision

MODIFIER

Nom final

hypotheses

Justification

Le concept doit être conservé.

Une hypothèse représente une proposition de travail formulée pendant l'enquête.

Elle ne doit pas être confondue avec :

  • une relation ;
  • une preuve ;
  • une note libre ;
  • une conclusion définitive.

Une hypothèse peut évoluer à mesure que de nouveaux éléments apparaissent.

Elle doit pouvoir être :

  • proposée ;
  • étudiée ;
  • soutenue ;
  • contredite ;
  • confirmée ;
  • rejetée ;
  • archivée.

Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de l'hypothèse
titre TEXT oui Titre court
description TEXT oui Description détaillée
confiance INTEGER oui Niveau de confiance de 0 à 100
evaluation TEXT non Justification du niveau de confiance
created_at TEXT oui Date de création
updated_at TEXT oui Dernière modification
status TEXT oui État logique de l'hypothèse

Niveau de confiance

La colonne :

confiance

est comprise entre :

0 et 100

Elle représente l'évaluation actuelle de l'enquêteur.

Exemples :

0   = aucune confiance
25  = faible
50  = plausible
75  = fortement probable
100 = confirmé

La confiance ne doit pas être calculée automatiquement dans la V1.

L'application pourra proposer des aides à l'évaluation plus tard, mais l'enquêteur reste responsable de la valeur enregistrée.


Évaluation

La colonne :

evaluation

explique pourquoi le niveau de confiance a été choisi.

Exemple :

Deux preuves indépendantes,
un numéro de téléphone commun
et une relation confirmée.

Cette colonne est facultative, mais fortement recommandée lorsque la confiance est modifiée.


Statuts autorisés

proposed
under_review
supported
contradicted
confirmed
rejected
archived

Signification :

proposed

Hypothèse nouvellement formulée.

under_review

Hypothèse en cours d'étude.

supported

Plusieurs éléments la soutiennent, sans confirmation définitive.

contradicted

Des éléments la contredisent.

confirmed

Hypothèse considérée comme confirmée.

rejected

Hypothèse écartée.

archived

Hypothèse conservée uniquement pour l'historique.


Contraintes prévues

PRIMARY KEY (id)
CHECK (length(trim(titre)) > 0)
CHECK (length(trim(description)) > 0)
CHECK (confiance BETWEEN 0 AND 100)
CHECK (
    evaluation IS NULL
    OR length(trim(evaluation)) > 0
)
CHECK (
    status IN (
        'proposed',
        'under_review',
        'supported',
        'contradicted',
        'confirmed',
        'rejected',
        'archived'
    )
)

Relation avec les preuves

Une hypothèse peut être :

  • soutenue ;
  • contredite ;
  • confirmée ;

par plusieurs preuves.

Une preuve peut intervenir dans plusieurs hypothèses.

La relation est stockée dans :

hypothese_preuves

avec un rôle :

supports
contradicts
confirms

Relation avec les entités

Une hypothèse peut concerner plusieurs entités.

La liaison est stockée dans :

hypothese_entites

Relation avec les relations

Une hypothèse peut s'appuyer sur des relations existantes ou les remettre en cause.

La liaison est stockée dans :

hypothese_relations

avec un rôle :

supports
contradicts

Relation avec les recherches

Une recherche peut :

  • produire une hypothèse ;
  • l'enrichir ;
  • la confirmer ;
  • la contredire.

La liaison est stockée dans :

recherche_hypotheses

avec un rôle :

created
enriched
confirmed
contradicted

Index prévus

CREATE INDEX idx_hypotheses_status
ON hypotheses(status);
CREATE INDEX idx_hypotheses_confiance
ON hypotheses(confiance);
CREATE INDEX idx_hypotheses_updated_at
ON hypotheses(updated_at);

Structure C cible

typedef struct Hypothese Hypothese;

Représentation privée envisagée :

struct Hypothese
{
    char *id;

    char *titre;
    char *description;

    int confiance;
    char *evaluation;

    char *created_at;
    char *updated_at;

    char *status;
};

Cette structure n'est pas implémentée dans le ticket #023.


Modifications par rapport au schéma historique

  • conservation du concept hypotheses ;
  • UUID comme clé primaire ;
  • ajout d'un titre obligatoire ;
  • ajout d'une description obligatoire ;
  • ajout d'un niveau de confiance ;
  • ajout d'une évaluation explicative ;
  • ajout d'un statut détaillé ;
  • ajout des dates techniques ;
  • ajout de relations vers les preuves, entités, relations et recherches ;
  • ajout de contraintes et d'index.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `hypotheses`

Nouvelle table categories

Décision

AJOUTER

Nom final

categories

Justification

Le schéma historique ne distinguait pas clairement la classification structurante des annotations libres.

Une catégorie sert à classer un objet métier dans un domaine principal.

Exemples :

  • OSINT ;
  • finance ;
  • réseaux sociaux ;
  • téléphonie ;
  • documents ;
  • infrastructure.

Une catégorie est stable, peu nombreuse et généralement administrée.

Elle ne doit pas être confondue avec un tag, qui constitue une annotation libre et potentiellement multiple.


Objets catégorisables dans la V1

Les catégories peuvent être attribuées à :

preuves
recherches
hypotheses

Chaque objet possède au maximum une catégorie.

Les entités disposent déjà de type_id.

Les relations disposent déjà de type_relation.

Aucune catégorie supplémentaire n'est donc prévue pour elles dans la V1.


Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID de la catégorie
nom TEXT oui Nom visible
description TEXT non Description libre
icone TEXT non Nom d'icône du thème GTK
couleur TEXT non Couleur hexadécimale
system INTEGER oui Catégorie fournie par l'application
created_at TEXT oui Date de création
updated_at TEXT oui Dernière modification
status TEXT oui État logique

Colonne system

system = 1

indique une catégorie livrée avec Labfy Investigation.

system = 0

indique une catégorie créée par l'utilisateur.

L'application pourra empêcher la suppression ordinaire d'une catégorie système.


Couleur

La couleur facultative utilise le format :

#RRGGBB

Exemple :

#FF0000

Le schéma vérifie uniquement la longueur et le préfixe.

La validation complète des caractères hexadécimaux sera aussi effectuée dans le code C.


Contraintes prévues

PRIMARY KEY (id)
UNIQUE (nom COLLATE NOCASE)
CHECK (length(trim(nom)) > 0)
CHECK (
    description IS NULL
    OR length(trim(description)) > 0
)
CHECK (
    icone IS NULL
    OR length(trim(icone)) > 0
)
CHECK (
    couleur IS NULL
    OR (
        length(couleur) = 7
        AND substr(couleur, 1, 1) = '#'
    )
)
CHECK (system IN (0, 1))
CHECK (
    status IN (
        'active',
        'archived',
        'deleted'
    )
)

Structure C cible

typedef struct Categorie Categorie;

Représentation privée envisagée :

struct Categorie
{
    char *id;

    char *nom;
    char *description;
    char *icone;
    char *couleur;

    gboolean system;

    char *created_at;
    char *updated_at;

    char *status;
};

Décision finale

AJOUTER LA TABLE `categories`

Table historique tags

Décision

MODIFIER

Nom final

tags

Justification

Le concept doit être conservé.

Un tag est une annotation libre et transversale.

Contrairement à une catégorie :

  • plusieurs tags peuvent être appliqués au même objet ;
  • les utilisateurs peuvent en créer librement ;
  • ils ne structurent pas le modèle métier principal.

Exemples :

  • urgent ;
  • à vérifier ;
  • escroquerie ;
  • Telegram ;
  • fraude ;
  • prioritaire.

Objets pouvant recevoir des tags

Dans la V1 :

preuves
recherches
entites
relations
hypotheses
chronologie

Colonnes finales proposées

Colonne Type Obligatoire Description
id TEXT oui UUID du tag
nom TEXT oui Nom visible
description TEXT non Description libre
couleur TEXT non Couleur hexadécimale
system INTEGER oui Tag fourni par l'application
created_at TEXT oui Date de création
updated_at TEXT oui Dernière modification
status TEXT oui État logique

Contraintes prévues

PRIMARY KEY (id)
UNIQUE (nom COLLATE NOCASE)
CHECK (length(trim(nom)) > 0)
CHECK (
    description IS NULL
    OR length(trim(description)) > 0
)
CHECK (
    couleur IS NULL
    OR (
        length(couleur) = 7
        AND substr(couleur, 1, 1) = '#'
    )
)
CHECK (system IN (0, 1))
CHECK (
    status IN (
        'active',
        'archived',
        'deleted'
    )
)

Structure C cible

typedef struct Tag Tag;

Représentation privée envisagée :

struct Tag
{
    char *id;

    char *nom;
    char *description;
    char *couleur;

    gboolean system;

    char *created_at;
    char *updated_at;

    char *status;
};

Modifications par rapport au schéma historique

  • conservation du concept ;
  • UUID comme clé primaire ;
  • unicité du nom sans tenir compte de la casse ;
  • ajout d'une description ;
  • ajout d'une couleur ;
  • distinction entre tags système et utilisateur ;
  • ajout des dates techniques ;
  • ajout de la suppression logique ;
  • ajout de tables de liaison explicites.

Décision finale

CONSERVER LE CONCEPT
MODIFIER LE SCHÉMA
CONSERVER LE NOM `tags`