← Tous les projets

Logiciel · Santé

NoBullFit

Une application de suivi de la santé et de la mise en forme axée sur la protection de la vie privée. Je développe l'ensemble du projet, le backend en Rust, l'interface en React, les applications mobiles et toute l'infrastructure moins visible qui relie le tout.

Année
2025 →
Rôle
Fondateur · De bout en bout
Statut
En ligne

Ce que fait NoBullFit

NoBullFit est un journal quotidien de santé. Vous pouvez y suivre vos repas, scanner un code-barres au lieu d'entrer treize chiffres, créer et partager des recettes, enregistrer vos activités et vos séries d'entraînement musculaire, suivre votre poids et votre consommation d'eau, puis gérer vos listes d'épicerie. Toutes ces données sont ensuite présentées dans un tableau de bord comprenant des graphiques, une série de jours consécutifs, un bilan hebdomadaire et un rapport PDF que vous pouvez remettre à un entraîneur ou à un médecin.

Chaque nouveau compte comprend un Guide qui transforme une page vide en un parcours simple. Définissez un objectif. Enregistrez votre première journée. Montez sur la balance. Une étape à la fois.

NoBullFit propose une version gratuite et une version Pro offerte par l'intermédiaire de Paddle. La version Pro est conçue pour planifier les jours et les semaines à venir. Elle permet de prévoir les repas et les entraînements à des dates futures, de copier une journée ou une semaine, de définir des objectifs de poids accompagnés de recommandations sur les macronutriments et d'une date projetée, de consulter des analyses hebdomadaires et de choisir les données à inclure dans les rapports.

Architecture

Le backend est développé en Rust. Il utilise axum pour la couche HTTP, sqlx avec PostgreSQL, ainsi que des migrations intégrées directement à l'exécutable et appliquées au démarrage. Une nouvelle copie du dépôt nécessite donc uniquement une instance PostgreSQL fonctionnelle. Le frontend est développé avec React 19, Vite, TypeScript et Tailwind. Il communique avec le backend au moyen d'une API JSON simple.

Les données chiffrées consultées chaque jour sont toutes calculées au même endroit. La tendance du poids, la dépense énergétique mesurée, l'échéancier vers l'objectif et les cibles de macronutriments proviennent d'une fonction pure qui n'accède pas à la base de données. Son comportement est donc défini par des tests unitaires plutôt que par ce qu'une requête pourrait retourner à un moment donné.

Chacune de ces données reste vide tant qu'elle ne repose pas sur une quantité suffisante de données réelles. L'application indique clairement ce qui manque encore au lieu d'afficher une estimation présentée comme certaine.

Chaque test du backend crée sa propre base de données PostgreSQL temporaire, applique les migrations, puis la supprime à la fin. La suite de tests s'exécute en parallèle sur de vraies requêtes SQL. Elle vérifie donc directement les contraintes et les suppressions en cascade plutôt qu'une simple imitation de leur comportement.

Le catalogue alimentaire

La recherche d'aliments utilise notre propre copie des données d'Open Food Facts. Quelques millions de produits sont importés dans PostgreSQL et interrogés directement à partir de cette base. Aucune API nutritionnelle externe n'est appelée chaque fois qu'un caractère est tapé. Cette approche est plus rapide et empêche également des services externes de savoir ce qu'un membre mange.

Les codes-barres sont décodés directement sur l'appareil. Le détecteur natif du navigateur est utilisé lorsqu'il est disponible, un lecteur WebAssembly est utilisé dans Safari et Firefox, et les applications mobiles utilisent le lecteur du système d'exploitation. Seuls les chiffres du code-barres sont transmis au serveur.

Lorsqu'un produit n'existe pas encore dans le catalogue, la personne qui tient l'emballage peut l'ajouter une seule fois. Tous les prochains scans de ce produit permettront ensuite de le retrouver.

Chaque aliment enregistré conserve son propre instantané des macronutriments. Une mise à jour du catalogue modifie les recherches futures, mais ne change jamais les valeurs enregistrées dans une journée passée.

Une base de code, trois applications

Les applications iOS et Android sont des coquilles Capacitor qui utilisent la même version du frontend que le site Web. Il n'existe aucune deuxième base de code réservée aux appareils mobiles.

Les quelques différences propres aux plateformes sont gérées par des vérifications effectuées à l'exécution dans la même version de l'application. Cela comprend la barre d'état, les téléchargements transmis par le menu de partage du système, le lecteur de codes-barres du système d'exploitation et, sur iOS, l'absence d'une option permettant d'acheter un abonnement conformément aux règles de l'App Store.

L'application iOS est également compatible avec Apple Santé. Elle peut lire le poids corporel et le nombre de pas, puis y inscrire les données nutritionnelles enregistrées dans NoBullFit. Cette intégration est optionnelle, gratuite et limitée à ces données. Aucune autre information n'est lue ni partagée.

Les entraînements ont volontairement été exclus. Les données d'énergie active de HealthKit les prennent déjà en compte. Les importer séparément entraînerait donc un double calcul dans le budget calorique que les utilisateurs consultent chaque matin.

Traçabilité

Chaque requête est enregistrée avec un identifiant, la méthode utilisée, le point d'accès, le statut et la durée. Ces renseignements sont écrits dans la console et dans des fichiers JSON avec rotation.

Les actions liées à la sécurité génèrent également leur propre événement d'audit. Cela comprend les connexions et les tentatives échouées, les changements de mot de passe ou d'adresse courriel, les exportations, les suppressions, les webhooks de facturation et les actions administratives.

L'identifiant de la requête est retourné dans les en-têtes de la réponse. Lorsqu'un utilisateur signale une erreur, il est donc possible de retrouver exactement les lignes de journal qui l'ont produite.

Les mots de passe, les jetons et les identifiants de session ne sont jamais inscrits dans les journaux. L'adresse du client est également déterminée par le serveur avant d'être enregistrée. Un en-tête falsifié ne peut donc pas corrompre les journaux ni fausser le limiteur de débit.

Protection de la vie privée dès la conception

Les mots de passe sont hachés avec Argon2id. Les sessions sont conservées côté serveur et seul le hachage SHA-256 du jeton est enregistré. Même en accédant à la base de données, il n'est donc pas possible d'en extraire une session fonctionnelle.

La création de compte, la connexion et la réinitialisation du mot de passe sont protégées par Cloudflare Turnstile plutôt que par un CAPTCHA qui doit établir le profil du visiteur pour fonctionner.

L'exportation des données est complète. Après confirmation du mot de passe et application d'une limite de fréquence, l'utilisateur peut obtenir une exportation JSON de chaque table contenant ses données.

La suite de tests en garantit l'exhaustivité. L'ajout d'une colonne à une table contenant des données utilisateur fait échouer la compilation jusqu'à ce qu'elle soit classée comme exportée ou explicitement exclue avec une justification écrite.

La suppression est précise. Elle peut viser une période déterminée ou l'ensemble des données, puis se propage correctement dans le schéma grâce aux suppressions en cascade.

Il est plus juste de décrire NoBullFit comme une plateforme soucieuse de la vie privée plutôt que comme une solution offrant une confidentialité maximale. Aucun outil de suivi n'est exécuté du côté client, bien que le réseau de diffusion placé devant le site comptabilise les requêtes. Les journaux contiennent également une adresse courriel et une adresse IP afin de permettre l'exploitation de la plateforme.

L'objectif est que chaque affirmation concernant la vie privée corresponde à un mécanisme concret dans le code plutôt qu'à un simple paragraphe dans une politique.

Points forts de l'architecture
  • Catalogue alimentaire autohébergé. Les données d'Open Food Facts sont importées dans PostgreSQL et interrogées directement à partir de cette base. Aucune API nutritionnelle externe ne voit les recherches.
  • Codes-barres décodés sur l'appareil. L'application utilise le détecteur natif, un lecteur WebAssembly ou le lecteur du système d'exploitation. Seuls les chiffres quittent le téléphone.
  • Analyses sans accès à la base de données. La tendance du poids, la dépense énergétique mesurée, la date projetée et les cibles de macronutriments sont calculées par une fonction pure encadrée par des tests unitaires.
  • De vraies bases de données pour les tests. Chaque test du backend crée, migre et supprime sa propre base de données PostgreSQL, le tout en parallèle.
  • Traçabilité par identifiant de requête. Chaque requête et chaque événement de sécurité sont enregistrés dans des fichiers JSON avec rotation, sans jamais y inscrire de données confidentielles.
  • Une exportation qui ne peut pas devenir incomplète. Une liste de règles et des tests font échouer la compilation lorsqu'une nouvelle colonne contenant des données utilisateur n'est ni exportée ni explicitement exclue.
  • Double protection pour l'administration. Un statut d'administrateur permanent est combiné à une élévation temporaire des privilèges qui prend fin lorsque l'opérateur quitte le site.
Stack
Rust · axum · React · TypeScript · PostgreSQL