L'app
Cosa dovrei eseguire?
Cosa eseguire, da un'installazione solo su browser fino a una configurazione domestica self-hosted
Questa pagina è tradotta automaticamente dalla documentazione in inglese.
Quattro livelli. Ognuno aggiunge una funzionalità e qualcosa che ora devi gestire tu. Parti dal basso e fermati non appena hai ciò che ti serve: la maggior parte delle persone si ferma al livello 0 o 1.
| Livello | Ottieni | Gestisci | File compose |
|---|---|---|---|
| 0 | Tracciamento dei pasti + scansioni con IA | Niente | nessuno |
| 1 | Lo stesso, sulla tua macchina | Un container stateless | docker/compose.yml |
| 2 | Il tuo diario su due dispositivi e, su un'istanza gestita, una spesa IA condivisa per la famiglia o l'organizzazione | + un database e un segreto | docker/topologies/compose.core.yml |
| 3 | Scansioni sul tuo hardware | + un runtime di inferenza per i modelli | docker/topologies/compose.inference.yml |
| 4 | Tutto quanto | Tutto quanto | docker/topologies/compose.full.yml |
Ogni file compose è annotato riga per riga; docker/topologies/README.md è la stessa mappa vista dal lato di compose.
A ogni livello, il server dell'app cerca anche i nomi dei cibi nel database alimentare di LowCarbCheck per le persone che lo usano. Invia i nomi, mai una foto o una voce del diario. Dal livello 1 in su è il tuo server a farlo: se più di una persona esegue scansioni, forniscigli una chiave gratuita. Vedi architecture.md.
Ogni comando qui sotto si esegue anche sotto Podman come podman compose. Su Ubuntu, quel sottocomando richiede il pacchetto podman-compose installato accanto. Vedi podman.md.
Livello 0: non eseguire nulla
Apri un'istanza esistente, come https://openplate.lowcarbcheck.org, e incolla la tua chiave del provider in Impostazioni → IA. Non è richiesta alcuna registrazione. Il tuo diario risiede nella memoria di quel browser e non raggiunge mai il server dell'istanza, quindi "usare l'istanza di qualcun altro" concede a quel gestore molto meno di quanto la frase suggerisca: vedi architecture.md. I nomi degli alimenti che scansioni o cerchi passano attraverso di essa, nel loro tragitto verso il database alimentare.
A questo livello non devi gestire nulla. Il browser conserva il diario, il browser chiama il provider con la chiave che vi hai incollato e il server del gestore si limita a inviare la pagina.
Sorgente del diagramma
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"]Ottieni: l'intero prodotto, in un minuto, al solo costo del tuo utilizzo dell'IA. Non Gestisci: nulla.
L'inconveniente, in tutta onestà: un'istanza dimostrativa pubblica non offre alcuna garanzia di disponibilità e nulla viene salvato per te. Il tuo diario si trova in quel browser, e cancellare i dati del browser lo cancella. Esporta regolarmente il JSON da Profilo → I tuoi dati, oppure passa al livello 1.
Livello 1: l'app sul tuo computer
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/compose.yml
docker compose -f compose.yml up -dAprilo comehttp://localhost:3000su quella macchina, oppure tramite HTTPS. Da un altro dispositivo,http://<its address>:3000mostra il diario. L'installazione dell'app, l'uso offline e la connessione a OpenRouter in un clic non funzionano lì. Vedi self-hosting.md.
Il livello 1 modifica un solo elemento. La pagina proviene da un container che esegui tu, e il percorso delle foto è esattamente quello indicato sopra.
Sorgente del diagramma
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"]Ottieni: l'app su hardware che controlli, aggiornabile quando vuoi, senza alcuna dipendenza da istanze altrui. Gestisci: un solo container. Nessun database, nessun passaggio di .env, nessun segreto da generare, nulla da migrare in caso di aggiornamento. Se si arresta, non perdi nulla, perché non memorizza nulla. File Compose: docker/compose.yml.
Questo è il punto di arrivo consigliato. Tutto ciò che segue aggiunge un carico di gestione concreto.
La guida completa si trova in self-hosting.md. Tratta HTTPS, che serve per l'installazione della PWA, la connessione con OpenRouter in un clic e l'accesso dal livello 2 in su.
Livello 2: aggiungi la sincronizzazione
Ottieni: un solo diario su tutti i tuoi dispositivi. Il vero caso d'uso è una persona, due dispositivi: un telefono e un portatile sempre allineati. L'uso in famiglia è secondario, e meno adatto: la sincronizzazione è legata all'account, quindi due persone che condividono lo stesso account condividono un unico diario invece di averne uno a testa. Due persone che desiderano diari separati hanno bisogno di due account, o semplicemente di due dispositivi al livello 1 senza alcuna sincronizzazione.
Il livello 2 aggiunge un secondo server con un database alle spalle. Ciascun dispositivo invia lo stesso blob cifrato e scarica quello dell'altro, mentre la foto parte comunque da ogni dispositivo verso il fornitore.
Sorgente del diagramma
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"| cloudGestisci: l'app, un servizio per gli account e un Postgres. Il salto di complessità è concreto: un servizio per gli account include un database di cui fare il backup, un SERVER_SECRET da conservare e utenti che possono rimanere chiusi fuori. Leggi il README di openplate-core prima di esporlo su Internet pubblico. File Compose: docker/topologies/compose.core.yml.
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 -dGli account richiedono una pagina sicura. Accesso, registrazione e apertura di un invito falliscono suhttp://<LAN address>non protetto. Usa HTTPS, con un nome di dominio o su una rete domestica senza di esso, oppurelocalhosttramite un tunnel ssh per una prova. Nessuno si registra da solo. Generi il primo invito sul server conADMIN_TOKEN, come mostra Crea il primo account. Una rete domestica senza nome di dominio ottiene HTTPS da Caddy con un certificato locale.
Il servizio memorizza ogni voce come testo cifrato e non riceve mai la tua password. Conserva il codice di recupero di ciascun account, sigillato da un proprio segreto, così se dimentichi la password puoi reimpostarla tramite un link (inviato via email, o consegnato a mano da te su un'istanza priva di posta) e recuperare il diario. Questo significa anche che chi gestisce il servizio può, in linea di principio, aprirvi un diario. sync.md illustra questo compromesso per intero, e lo fa anche l'app prima di completare la configurazione della sincronizzazione.
Il server può anche coprire una spesa condivisa per l'IA, se lo attivi. Imposta INSTANCE_MODE=managed e l'istanza diventa un'istanza gestita da un amministratore per un nucleo familiare o un'organizzazione: gli amministratori invitano persone da /admin (o tramite l'API di amministrazione), impostano una quota giornaliera per ciascun account, e ogni scansione autenticata passa attraverso il proxy AI interno del server centrale: nessun servizio separato, nessun link di invito distinto. La posta è opzionale: /admin mostra sempre l'invito come un link che un amministratore può copiare e inviare, con o senza email. Vedi configuration.md#managed-instances e family-setup.md per capire quando conviene attivare questa opzione invece delle sottochiavi del provider.
Su un'istanza gestita, lo stesso server elabora anche la scansione. Un membro che ha effettuato l'accesso invia la foto al proxy IA, il server la scala dalla quota giornaliera di quell'account e inoltra la richiesta alla destinazione indicata da chi gestisce il sistema.
Sorgente del diagramma
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"]Permetti ai membri di invitarsi a vicenda, ma mantieni basso il numero. Su un'istanza gestita, imposta MEMBER_INVITE_DAILY_AI_LIMIT e MEMBER_INVITE_ALLOWANCE_DAYS sul server centrale. Questo consente a un membro ordinario di invitare qualcuno senza chiedertelo prima. Se paghi tu per la chiave del provider, imposta anche MEMBER_INVITE_LIFETIME_CAP=2. Il valore predefinito è 5, adatto a un'istanza in cui il costo dell'AI è condiviso. Impostarlo a 2 è sufficiente per un partner e un amico, e mantiene la crescita abbastanza lenta da poterla monitorare. Vedi configuration.md#member-invites.
Livello 3: aggiungi l'inferenza self-hosted
Il livello 3 esegue la scansione sul tuo hardware. Il browser invia le foto direttamente al container di inferenza. I tuoi browser devono poter risolvere l'indirizzo di quel container. Il modello individua ciascun alimento e stima il peso in grammi. openplate-inference legge i macronutrienti dalla fonte di dati alimentari che hai configurato.
Sorgente del diagramma
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"]Ottieni: scansioni locali del piatto senza un account AI nel cloud, tariffe per scansione o traffico di foto in uscita. L'immagine del container include già per impostazione predefinita un estratto di USDA FoodData Central, così openplate-inference cerca i macronutrienti invece di inventarli. Gestisci: un runtime del modello e alcuni gigabyte di pesi, oltre a tutto ciò che serve per rendere l'endpoint raggiungibile dai tuoi browser (la foto va dal dispositivo all'endpoint, quindi un hostname di compose qui non funziona). File Compose: docker/topologies/compose.inference.yml.
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 -dSostituisci 192.168.1.20 con l'indirizzo del tuo server. Quando l'app si trova su HTTPS dietro un reverse proxy, anche l'indirizzo di inferenza deve usare https://, e TRUST_PROXY deve essere impostato su 1. self-hosting.md illustra la procedura.
Questo livello è pensato per due tipi di persone:
- L'hardware è tuo. Una macchina con GPU, oppure una con una CPU sufficientemente potente.
- Gestisci già un runtime del modello. Se hai già attivo llama.cpp, Ollama o vLLM su GPU, imposta
MODEL_PROFILE=externaleMODEL_RUNTIME_URL: openplate-inference non scarica nulla e non avvia un secondo modello, ma si limita a usare quello che hai. Controlla prima la matrice di supporto; la build CPU di vLLM non può eseguire questa operazione.
Trasparenza sull'hardware. Il profilo piccolo lite richiede 2.0 GiB di pesi e 8+ core moderni con AVX2 e 4 GB di RAM libera su una macchina con sola CPU; il profilo più grande quality richiede 5.8 GiB di pesi e almeno 5.8 GiB di VRAM. Le scansioni su CPU richiedono da pochi secondi a qualche minuto, e il throughput non migliora con la concorrenza: pianifica la capacità come se la macchina operasse in modo seriale. I valori misurati, per ciascun profilo, si trovano in docs/hardware.md di openplate-inference. Leggili prima di acquistare qualsiasi cosa.
Una volta in funzione, puoi distribuire una chiave a ciascuna persona (Impostazioni → AI → Compatibile con OpenAI) oppure impostare DEFAULT_INFERENCE_BASE_URL e le relative opzioni per permettere a ogni visitatore di connettersi con un tocco, con l'avvertenza che DEFAULT_INFERENCE_API_KEY è incorporata nella pagina e leggibile da chiunque possa aprire l'app. Vedi configuration.md.
Il server centrale e l'inferenza sono livelli distinti
È facile confonderli e si compongono tra loro.
- openplate-inference è il livello di calcolo. Risponde alla domanda cosa c'è su questo piatto. Include un runtime del modello e i pesi, e richiede hardware.
- openplate-core, su un'istanza gestita, è il livello multi-tenant. Risponde a chi ha il permesso di spendere, quanto, e come posso revocarlo. Non include alcun modello e inoltra tutto.
Punta il proxy AI di un'istanza gestita verso la tua macchina di inferenza (UPSTREAM_BASE_URL di openplate-core, con una delle API_KEYS del servizio di inferenza come UPSTREAM_API_KEY) e ottieni entrambi i vantaggi: scansioni sul tuo hardware, protette da quote per account. Puntalo invece verso un provider cloud e otterrai una spesa condivisa senza gestire hardware. In entrambi i casi, lo stesso server centrale gestisce anche il diario: la sincronizzazione e il proxy AI sono ora un unico servizio, non due (architecture.md).
Livello 4: tutto
Il livello 4 unisce i due livelli precedenti. Non introduce nulla di nuovo.
Sorgente del diagramma
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"| infOttieni: gradino 2 e gradino 3 insieme (il tuo diario su ogni dispositivo, scansioni elaborate sul tuo hardware, senza inviare nulla a terzi). Gestisci: tutto. App, server centrale, Postgres, runtime del modello e indirizzi raggiungibili da browser per due di essi. File Compose: docker/topologies/compose.full.yml. La sua intestazione elenca le righe .env: quelle del gradino 2 e del gradino 3 combinate.
Podman esegue questo nello stesso modo: podman compose -f compose.full.yml up -d.
Non c'è nulla di nuovo da imparare a questo livello. È l'unione dei due precedenti, con lo stesso SERVER_SECRET, lo stesso obbligo di backup e gli stessi requisiti hardware minimi.
Condividere una spesa invece di un server
Se il motivo per cui hai scalato questa scala era "il mio nucleo familiare ha bisogno di più di una chiave AI", la prima risposta non è affatto un gradino. Si risolve a livello di provider, con chiavi individuali e limiti di spesa per persona, senza richiedere software aggiuntivo. family-setup.md descrive i passaggi e, se il tuo provider non rilascia sottochiavi con tetti di spesa, indica come alternativa un server centrale gestito (gradino 2, con INSTANCE_MODE=managed).