Aller au contenu

02 / 04 Fonctionnel · migration HDS requise avant mise en service

NutriFollow

SaaS de suivi pour diététiciens indépendants, sur nutrifollow.fr, conçu et construit seul. Il est fonctionnel ; trois conditions, dont la migration vers un hébergement certifié HDS, restent à remplir avant d'accueillir des patients réels. Personne ne l'utilise aujourd'hui.

Coupe de l'application et de ses frontières Praticien sur ordinateur et patient sur téléphone utilisent la même application Laravel servie par Inertia. Le contrôleur des patients et une portée globale par cabinet isolent les données. Reverb pousse le chat et le journal en temps réel. Les demandes d'IA passent par une file Redis vers Groq. Trois conditions bloquent l'accueil de patients réels : hébergement HDS, pseudonymisation avant l'appel au service d'inférence, chiffrement étendu. Praticien ordinateur 3 Patient PWA, téléphone 3 Laravel 13 · Inertia / Vue une application, deux interfacespas d'API REST séparée 3 PatientController e-mail unique,verrouillé aprèsactivation 1 Reverb chat, journalen direct 3 File Redis jobs IA → GroqJSON validé 4 MySQL portée par cabinet 1 Mise en service HDS, pseudonymisation, chiffrement :trois conditions avant patients réels 2 Coupe · NutriFollow Laravel 13
Coupe de l'application et de ses frontières. Les numéros renvoient aux passages du texte. Trait tireté : système externe ; hachures : contrainte.

Le problème

Le suivi diététique se joue surtout entre les consultations : ce que le patient mange, son poids, ses questions, ses rendez-vous. NutriFollow met le praticien et ses patients sur le même outil. Le praticien construit des plans alimentaires à partir de la table de composition CIQUAL, suit le journal et la courbe de poids de chaque patient, gère ses créneaux. Le patient tient son journal depuis son téléphone (repas en photo, faim, humeur, sommeil), réserve un rendez-vous et écrit à son praticien.

Ce sont des données de santé, dont chaque praticien est responsable. Le produit doit donc isoler strictement les cabinets les uns des autres, et ne rien laisser passer d'un compte à l'autre.

Ce qui rendait la solution évidente inutilisable

Le comportement le plus naturel à écrire était aussi le plus dangereux. Quand un praticien ajoute un patient dont l'adresse e-mail a déjà un compte, le plus simple est de rattacher ce compte existant. C'est ce que faisait le code, sans regarder à qui appartenait ce compte, même à un autre praticien. Comme l'e-mail d'un patient pouvait ensuite être modifié sans contrainte d'unicité, la chaîne était complète : rattacher la victime, remplacer son e-mail par une adresse qu'on contrôle, demander une réinitialisation du mot de passe ou se connecter avec Google. Le compte changeait de propriétaire.

La faille a été trouvée lors d'un audit de sécurité interne, le 29 septembre 2026, et corrigée le jour même. Une adresse déjà connue est désormais refusée à la création. L'e-mail d'un patient n'est plus modifiable par le praticien dès que le patient a activé son compte ; avant, il le reste, pour corriger une faute de frappe. La connexion Google exige une adresse vérifiée par Google et ne se lie plus à un compte local non vérifié, ce qui ferme une variante par pré-création de compte. Des tests de non-régression couvrent chacun de ces cas.

Deuxième évidence inutilisable : mettre en service, tel quel, un produit qui fonctionne. Trois contraintes identifiées conditionnent l'accueil de patients réels. L'hébergement : en France, des données de santé hébergées pour le compte de tiers doivent l'être chez un hébergeur certifié HDS, et le produit doit migrer vers l'un d'eux. La pseudonymisation : les données d'un patient doivent être pseudonymisées avant tout appel au service d'inférence, qui est un tiers. Le chiffrement : déjà appliqué aux traitements et aux notes médicales, il doit s'étendre au reste du dossier médical. Le produit fonctionne ; ces trois points décident du moment où il pourra servir.

Ce qui a été choisi, et ce que ça a coûté

Une seule application Laravel, servie par Inertia et Vue, porte les deux interfaces : celle du praticien, pensée pour l'ordinateur, et celle du patient, installable sur téléphone comme PWA. Il n'y a pas d'API REST séparée, pas d'application native, pas de multi-cabinet (un praticien, un compte), et la visio passe par un lien Google Meet externe. Le temps réel passe par Reverb : messagerie, saisies du journal qui arrivent chez le praticien pendant qu'il le consulte, notifications. Les rappels de rendez-vous partent la veille par mail, SMS et notification push.

Le coût de la PWA est apparu pendant l'audit : le service worker gardait en cache, pendant 24 heures, des pages et des réponses contenant des données médicales, encore lisibles après déconnexion. Il ne met plus en cache que les fichiers statiques. Il n'y a donc pas de mode hors ligne pour les données, et c'est voulu.

L'IA (proposition de plan, suggestions de repas, analyse du journal, résumé avant rendez-vous) passe par Groq, choisi pour son coût. Chaque demande est un job asynchrone dans une file Redis. Le JSON renvoyé est validé avant affichage, et le résultat est présenté comme une proposition à valider par le praticien, jamais comme une prescription.

L'isolation entre cabinets repose sur une portée globale appliquée aux modèles. L'audit a montré sa limite : deux fuites entre cabinets, classées hautes, passaient à côté. L'export RGPD incluait des conversations d'autres cabinets, et la duplication d'un plan acceptait l'identifiant du patient d'un autre praticien. Une portée globale filtre les requêtes qu'on écrit, elle ne valide pas l'identifiant qu'un formulaire envoie. Les deux corrections sont une règle de validation dédiée et un filtre explicite sur l'export. Le même audit a déplacé les photos de santé d'un disque public vers un disque privé, servi derrière des autorisations. Il a aussi mis à zéro les alertes de composer audit et npm audit.

Ce qui manque pour lancer le produit n'est pas que technique : le démarchage n'a pas été fait. J'ai donné la priorité à l'activité freelance.

Vérifiable

Chiffres déclarés, dépôt privé :

Tests
184 Pest · 44 Vitest · 70 Dusk au 2 octobre 2026