Aller au contenu
openplate

L'application

Que dois-je exécuter ?

Quoi exécuter, de la simple utilisation dans le navigateur jusqu'au foyer auto-hébergé

Cette page est traduite automatiquement à partir de la documentation en anglais.

Quatre échelons. Chacun apporte une fonctionnalité et ajoute un élément que tu dois désormais administrer. Commence par le bas et arrête-toi dès que tu as ce qu'il te faut : la plupart des gens s'arrêtent à l'échelon 0 ou 1.

ÉchelonCe que tu obtiensCe que tu gèresFichier Compose
0Suivi des assiettes + analyses IARienaucun
1La même chose, sur ta propre machineUn conteneur sans étatdocker/compose.yml
2Ton journal sur deux appareils, et, sur une instance gérée, une facture IA partagée pour un foyer ou une organisation+ une base de données et un secretdocker/topologies/compose.core.yml
3Les analyses sur ton propre matériel+ un moteur d'exécution de modèlesdocker/topologies/compose.inference.yml
4La totalitéLa totalitédocker/topologies/compose.full.yml

Chaque fichier Compose est annoté ligne par ligne ; docker/topologies/README.md propose la même vue d'ensemble du point de vue de Compose.

À chaque niveau, le serveur de l'application consulte également la base de données alimentaire LowCarbCheck pour trouver les noms d'aliments des personnes qui l'utilisent. Il envoie des noms, jamais une photo ni une entrée du journal. À partir du niveau 1, c'est ton serveur qui s'en charge : si plusieurs personnes scannent, fournis-lui une clé gratuite. Voir architecture.md.

Chaque commande ci-dessous fonctionne aussi sous Podman avec podman compose. Sur Ubuntu, cette sous-commande nécessite d'installer le paquet podman-compose à côté. Voir podman.md.

Échelon 0 : ne rien exécuter

Ouvre une instance existante, par exemple https://openplate.lowcarbcheck.org, et colle ta propre clé de fournisseur dans Paramètres → IA. Il n'y a pas d'inscription. Ton journal réside dans le stockage de ce navigateur et n'atteint jamais le serveur de l'instance, de sorte qu'« utiliser l'instance de quelqu'un d'autre » donne bien moins à cet opérateur que l'expression ne le laisse penser : voir architecture.md. Les noms des aliments que tu scannes ou recherches transitent bien par lui, en route vers la base de données alimentaire.

À cet échelon, tu n'as rien à faire tourner. Le navigateur conserve le journal, le navigateur appelle le fournisseur avec la clé que tu y as collée, et le serveur de l'administrateur se contente d'envoyer la page.

À l'échelon zéro, le navigateur conserve le journal et appelle directement un fournisseur cloud avec ta clé.
Source du schéma
flowchart LR
  host["Someone else's instance"] -->|"HTML and JS"| browser["Your browser"]
  browser --- diary["Diary in this browser"]
  browser -->|"photo and your key"| cloud["Cloud AI provider"]

Tu obtiens : l'ensemble du produit, en une minute, pour le seul coût de ton utilisation de l'IA. Tu gères : rien.

La contrepartie, en toute franchise : une instance de démonstration publique n'offre aucune garantie de disponibilité et rien n'y est sauvegardé pour toi. Ton journal se trouve dans ce navigateur, et vider le navigateur le supprime. Exporte régulièrement le JSON depuis Profil → Tes données, ou passe au niveau 1.

Niveau 1 : l'application sur ta propre machine

Outil de conteneurs
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/compose.yml
docker compose -f compose.yml up -d
Ouvre-le comme http://localhost:3000 sur cette machine, ou via HTTPS. Depuis un autre appareil, http://<its address>:3000 affiche le journal. L'installation de l'application, l'utilisation hors ligne et la connexion OpenRouter en un clic n'y fonctionnent pas. Voir self-hosting.md.

Le niveau 1 modifie une seule machine. La page provient d'un conteneur que tu exécutes, et le chemin de la photo est exactement le même que ci-dessus.

Au niveau un, la page provient de ton propre conteneur sans état, et le trajet de la photo reste inchangé.
Source du schéma
flowchart LR
  app["openplate app, your box"] -->|"HTML and JS"| browser["Your browser"]
  browser --- diary["Diary in this browser"]
  browser -->|"photo and your key"| cloud["Cloud AI provider"]

Tu obtiens : l'application sur du matériel que tu contrôles, que tu peux mettre à jour selon ton propre calendrier, sans dépendre de l'instance de quelqu'un d'autre. Tu gères : un seul conteneur. Pas de base de données, pas d'étape .env, aucun secret à générer, rien à migrer lors d'une mise à jour. S'il s'arrête, rien n'est perdu, car il ne stocke rien. Fichier Compose : docker/compose.yml.

C'est le point d'arrêt recommandé. Tout ce qui suit ajoute un réel travail d'exploitation.

Le guide pas-à-pas complet se trouve dans self-hosting.md. Il couvre HTTPS, dont tu as besoin pour installer la PWA, connecter OpenRouter en un clic et te connecter à partir du niveau 2.

Niveau 2 : ajouter la synchronisation

Tu obtiens : un seul journal sur l'ensemble de tes appareils. Pour le dire honnêtement, cela sert surtout à une personne, deux appareils : un téléphone et un ordinateur portable qui restent synchronisés. L'usage pour les familles est le second cas d'usage, et le moins adapté : la synchronisation fonctionne par compte, donc deux personnes qui partagent un même compte partagent un seul journal au lieu d'en avoir un chacune. Deux personnes qui souhaitent des journaux distincts doivent créer deux comptes, ou tout simplement utiliser deux appareils au niveau 1 sans aucune synchronisation.

Le niveau 2 ajoute un second serveur avec une base de données derrière. Chaque appareil envoie le même bloc chiffré et récupère celui de l'autre appareil, et la photo continue de partir de chaque appareil vers le fournisseur.

Au deuxième échelon, les deux appareils envoient un unique bloc chiffré au serveur central, et la photo part toujours directement vers le fournisseur.
Source du schéma
flowchart LR
  app["openplate app"] -->|"HTML and JS"| phone["Phone"]
  app -->|"HTML and JS"| laptop["Laptop"]
  phone -->|"ciphertext"| sync["openplate-core"]
  laptop -->|"ciphertext"| sync
  sync --> db[("Postgres")]
  phone -->|"photo and your key"| cloud["Cloud AI provider"]
  laptop -->|"photo and your key"| cloud

Tu gères : l'application, un service de comptes et une base Postgres. C'est une vraie étape supplémentaire : un service de comptes implique une base de données qu'il faut sauvegarder, un SERVER_SECRET à conserver, et des utilisateurs qui risquent de bloquer leur accès. Lis README d'openplate-core avant de l'exposer sur l'internet public. Fichier Compose : docker/topologies/compose.core.yml.

Outil de conteneurs
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/topologies/compose.core.yml
echo "SERVER_SECRET=$(openssl rand -hex 32)" >> .env
echo "ADMIN_TOKEN=$(openssl rand -hex 32)" >> .env
echo "PUBLIC_APP_URL=https://openplate.example.com"  >> .env
echo "PUBLIC_SYNC_URL=https://sync.example.com"      >> .env
echo "TRUST_PROXY=1"                                 >> .env  # 1 behind one reverse proxy, 0 with none
docker compose -f compose.core.yml up -d
Les comptes nécessitent une page sécurisée. La connexion, l'inscription et l'ouverture d'une invitation échouent sur du http://<LAN address> simple. Utilise HTTPS, avec un nom de domaine ou sur un réseau local sans domaine, ou localhost via un tunnel ssh pour un test. Personne ne s'inscrit de manière autonome. Tu génères la première invitation sur le serveur avec ADMIN_TOKEN, comme le montre Créer le premier compte. Un réseau local sans nom de domaine obtient HTTPS via Caddy avec un certificat local.

Le service enregistre chaque entrée sous forme de texte chiffré et ne reçoit jamais ton mot de passe. Il conserve le code de récupération de chaque compte, scellé sous son propre secret, afin qu'un mot de passe oublié soit réinitialisé par un lien (envoyé par e-mail, ou distribué par toi sur une instance sans e-mail) et que le journal revienne. Cela signifie aussi que l'opérateur du service peut, en théorie, ouvrir un journal dessus. sync.md détaille ce compromis, tout comme l'application avant de terminer la configuration de la synchronisation.

Le serveur peut aussi prendre en charge une facture d'IA partagée, si tu l'actives. Définis INSTANCE_MODE=managed et l'instance devient une instance gérée par un administrateur pour un foyer ou une organisation : les administrateurs invitent des personnes depuis /admin (ou via l'API d'administration), attribuent un quota quotidien à chaque compte, et chaque analyse effectuée par un membre connecté passe par le propre proxy IA du serveur central : aucun service distinct, aucun lien d'invitation séparé. Le courrier électronique est facultatif : /admin affiche systématiquement l'invitation sous forme de lien qu'un administrateur peut copier et transmettre, qu'un e-mail soit envoyé ou non. Consulte configuration.md#managed-instances et family-setup.md pour savoir quand cette option est préférable à l'usage de sous-clés chez le fournisseur.

Sur une instance gérée, ce même serveur prend aussi en charge l'analyse. Un membre connecté envoie la photo au proxy IA, le serveur la décompte du quota quotidien de ce compte, puis transmet la requête au service désigné par l'administrateur.

Sur une instance gérée, le membre connecté analyse ses repas via le proxy IA du serveur central, qui décompte la requête d'un quota journalier.
Source du schéma
flowchart LR
  browser["Member's browser"] -->|"ciphertext"| sync["openplate-core, managed"]
  browser -->|"photo"| sync
  sync --- quota["Daily allowance per account"]
  sync -->|"photo"| upstream["Cloud provider, or inference"]

Permets aux membres de s'inviter entre eux, tout en limitant le volume. Sur une instance gérée, active MEMBER_INVITE_DAILY_AI_LIMIT et MEMBER_INVITE_ALLOWANCE_DAYS sur le serveur central. Cela permet à un membre ordinaire d'inviter quelqu'un sans te demander l'autorisation au préalable. Si tu paies la clé du fournisseur, configure également MEMBER_INVITE_LIFETIME_CAP=2. La valeur par défaut est 5, ce qui convient à une instance où la facture d'IA est partagée. La régler sur 2 suffit pour un partenaire et un ami, tout en maintenant une croissance assez lente pour être surveillée. Voir configuration.md#member-invites.

Niveau 3 : ajouter l'inférence auto-hébergée

Le niveau 3 exécute l'analyse sur ton matériel. Le navigateur envoie directement les photos au conteneur d'inférence. Tes navigateurs doivent résoudre l'adresse de ce conteneur. Le modèle identifie chaque aliment et estime son poids en grammes. openplate-inference lit les macros depuis ta source d'aliments configurée.

Au niveau trois, le navigateur envoie la photo à ton propre conteneur d'inférence, qui recherche les macros dans sa source d'aliments configurée.
Source du schéma
flowchart LR
  app["openplate app"] -->|"HTML and JS"| browser["Your browser"]
  browser --- diary["Diary in this browser"]
  browser -->|"photo, browser reachable address"| inf["openplate-inference"]
  inf --- weights["Model runtime and weights"]
  inf --- usda["Configured food data, USDA by default"]

Tu obtiens : analyses locales d'assiettes sans compte d'IA dans le cloud, sans frais par analyse, ni trafic sortant de photos. L'image du conteneur intègre par défaut un extrait de USDA FoodData Central, openplate-inference recherche donc les macros au lieu de les inventer. Tu gères : un environnement d'exécution de modèle et quelques gigaoctets de poids, plus tout ce qui est nécessaire pour rendre le point de terminaison accessible depuis tes navigateurs (la photo va de l'appareil au point de terminaison, un nom d'hôte compose ne fonctionne donc pas ici). Fichier Compose : docker/topologies/compose.inference.yml.

Outil de conteneurs
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/topologies/compose.inference.yml
echo "INFERENCE_API_KEY=opk_$(openssl rand -hex 24)" >> .env
echo "PUBLIC_APP_URL=http://192.168.1.20:3000" >> .env
echo "PUBLIC_INFERENCE_URL=http://192.168.1.20:8300/v1" >> .env
echo "TRUST_PROXY=0" >> .env  # 0 with no reverse proxy, 1 behind one
docker compose -f compose.inference.yml up -d

Remplace 192.168.1.20 par l'adresse de ton serveur. Dès que l'application est en HTTPS derrière un proxy inverse, l'adresse d'inférence doit aussi utiliser https://, et TRUST_PROXY doit être défini sur 1. self-hosting.md détaille la procédure.

Ce niveau s'adresse à deux types de profils :

  • Tu possèdes le matériel. Une machine avec GPU, ou une machine avec CPU raisonnablement puissant.
  • Tu fais déjà tourner un moteur d'exécution de modèles. Si tu fais déjà tourner llama.cpp, Ollama ou vLLM sur GPU, définis MODEL_PROFILE=external et MODEL_RUNTIME_URL : openplate-inference ne télécharge alors rien, ne démarre pas de second modèle et se contente d'encapsuler ce que tu as déjà. Vérifie d'abord la matrice de compatibilité ; la version CPU de vLLM ne peut pas faire tourner cela.

Rigueur matérielle. Le petit profil lite représente 2,0 Gio de poids et nécessite 8 cœurs modernes ou plus avec AVX2 et 4 Go de RAM disponible sur une machine sans GPU ; le profil quality, plus lourd, représente 5,8 Gio de poids et un plancher de 5,8 Gio de VRAM. Les analyses sur CPU prennent de quelques secondes à quelques minutes, et le débit n'augmente pas avec la concurrence : dimensionne tes ressources comme si les requêtes étaient traitées en série. Les mesures réelles, profil par profil, sont dans docs/hardware.md de openplate-inference. Lis ce document avant d'acheter quoi que ce soit.

Une fois en service, tu peux soit donner une clé à chaque personne (Paramètres → IA → Compatible OpenAI), soit configurer DEFAULT_INFERENCE_BASE_URL et les variables associées pour que chaque visiteur se connecte en un clic, à la réserve près que DEFAULT_INFERENCE_API_KEY est intégré dans la page et lisible par quiconque peut ouvrir l'application. Voir configuration.md.

Le serveur central et l'inférence sont des couches distinctes

On les confond facilement et elles se combinent.

  • openplate-inference constitue la couche de calcul. Il répond à la question qu'y a-t-il dans cette assiette. Il embarque un moteur d'exécution de modèles ainsi que les poids, et il a besoin de matériel.
  • openplate-core, sur une instance gérée, forme la couche de gestion des accès. Il répond qui a le droit de dépenser, combien, et comment révoquer cet accès. Il n'embarque aucun modèle et relaie tout.

En faisant pointer le proxy IA d'une instance gérée vers ta machine d'inférence (UPSTREAM_BASE_URL de openplate-core, avec l'une des API_KEYS du service d'inférence comme UPSTREAM_API_KEY), tu obtiens les deux : des analyses sur ton propre matériel, précédées de quotas par compte. En le dirigeant plutôt vers un fournisseur cloud, tu obtiens des dépenses partagées sans matériel supplémentaire. Dans les deux cas, ce même serveur central achemine aussi le journal : la synchronisation et le proxy IA forment désormais un seul service, et non plus deux (architecture.md).

Niveau 4 : la totale

Le niveau 4 réunit les deux niveaux précédents. Rien de nouveau n'y apparaît.

Le niveau quatre combine les niveaux deux et trois : un seul journal sur chaque appareil et des analyses sur ton propre matériel.
Source du schéma
flowchart LR
  app["openplate app"] -->|"HTML and JS"| phone["Phone"]
  app -->|"HTML and JS"| laptop["Laptop"]
  phone -->|"ciphertext"| sync["openplate-core"]
  laptop -->|"ciphertext"| sync
  sync --> db[("Postgres")]
  phone -->|"photo"| inf["openplate-inference"]
  laptop -->|"photo"| inf

Tu obtiens : les échelons 2 et 3 réunis (ton journal sur chaque appareil, analysé sur ton propre matériel, sans rien envoyer à aucun tiers). Tu gères : la totalité. L'application, le serveur central, Postgres, le moteur d'inférence, et des adresses accessibles par navigateur pour deux d'entre eux. Fichier Compose : docker/topologies/compose.full.yml. Son en-tête liste les lignes .env : celles des échelons 2 et 3 combinés.

Podman lance ceci de la même manière : podman compose -f compose.full.yml up -d.

Il n'y a rien de nouveau à apprendre à ce niveau. C'est l'union des deux précédents, avec le même SERVER_SECRET, la même obligation de sauvegarde et le même plancher matériel.

Partager une facture plutôt qu'un serveur

Si tu as gravi ces échelons pour répondre au besoin « mon foyer a besoin de plusieurs clés d'IA », la réponse première n'est pas du tout un échelon. La solution se règle chez le fournisseur, avec des clés et des plafonds de dépenses par personne, sans nécessiter de logiciel supplémentaire. family-setup.md détaille la marche à suivre et propose, si ton fournisseur ne délivre pas de sous-clés plafonnées, un serveur central géré comme solution de repli (échelon 2, avec INSTANCE_MODE=managed).

Modifier cette page sur GitHub