Auto-găzduire

Node și un director în care se poate scrie sunt singura cerință. Afirmațiile de mai jos au fost citite din fișierele numite alături; tot ce nu a putut fi confirmat nu se află aici.

Puncte de intrare

  • package.json necesită Node >= 22.5. Depozitul SQLite este modulul node:sqlite al Node.
  • Nu există deloc o listă de dependențe: aplicațiile, pachetele, scripturile și testele rulează pe biblioteca standard a Node, iar Dockerfile nu instalează nimic.
  • Există trei procese; fiecare deschide baza de date și rulează migrarea la pornire.
node apps/web/server/main.mjs                   # hub + console + embedded API
API_EMBED=false node apps/api/src/main.mjs    # API only, no pages
node apps/worker/src/run.mjs                  # probes, latency, rollup, housekeeping

Depozit de date

DATABASE_URL=sqlite:./data/llmeu.db nu necesită server, iar directorul și fișierul sunt create la prima deschidere. Handle-ul rulează în modul WAL cu cheile străine activate și un timeout de ocupare de 5 s.

packages/shared/src/db/schema.sql este schema. Este doar de creare și idempotentă, astfel încât rerularea este sigură; o coloană adăugată la un tabel existent trebuie de asemenea declarată în pasul aditiv din packages/shared/src/db/index.mjs.

Un URL postgres:// urmează o ramură separată de adaptor. Acea ramură necesită pachetul pg, care nu este o dependență declarată, iar antetul schema.sql spune să tratați calea Postgres ca neverificată până când există un driver real și o migrare testată. SQLite este calea pe care rulează acest depozit.

CLI pentru operator

node scripts/db.mjs citește DATABASE_URL și nu necesită servicii. Comenzi:

Comandă Ce face
node scripts/db.mjs migrateAplică schema.sql și raportează numărul de instrucțiuni.
node scripts/db.mjs seedInserează sau reîmprospătează catalogul și organizația demo și scrie cheia demo în data/demo-key.txt cu modul 0600.
node scripts/db.mjs resetDoar pentru SQLite: elimină fișierul bazei de date și însoțitorii săi -wal și -shm, apoi migrează și populează. Pe Postgres refuză.
node scripts/db.mjs statusAfișează motorul, numărul de tabele și numărul de rânduri al fiecărui tabel nevid.
node scripts/db.mjs demo-keyEmite o cheie nouă pentru organizația demo și o afișează o dată.
node scripts/db.mjs passwd <email>Setează parola unui utilizator de pe server și șterge fiecare sesiune a acelui utilizator. Aceasta este calea de recuperare când nu este configurat un transport de email. Fără --password, generează una și o afișează o dată.

Mediu

Grupate așa cum le grupează .env.example.

Grup Variabile Note
URL-uri și porturiAPP_URL, API_URL, WEB_PORT, API_PORT, API_EMBED, PUBLIC_API_URLPortul API implicit este 8088, nu 8080. API_EMBED=false este pentru o implementare care rulează API-ul ca proces propriu; PUBLIC_API_URL este baza afișată în fragmentele de documentație.
Depozit de dateDATABASE_URL, REDIS_URLDATABASE_URL selectează depozitul; REDIS_URL este rezervat pentru coada de lucrări și nu este citit de calea de cod v1.
InferențăINFERENCE_MOCK, GPU_ENABLED, VLLM_BASE_URL, VLLM_REGION, VLLM_MODEL_VERSION, VLLM_BASE_URL_<REGION>, DEFAULT_REGION, PARTNER_*Vezi secțiunea de inferență de mai jos; PARTNER_* acoperă URL-ul de bază al partenerului, regiunea, clasa de suveranitate și cheia API.
FacturareFX_EUR_USD, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETRegistrul este ținut în micro-USD, iar EUR este derivat pentru afișare la FX_EUR_USD.
E-mailMAIL_FROM, MAIL_SMTP_HOST, MAIL_SMTP_PORT, MAIL_SMTP_SECURE, MAIL_SMTP_USER, MAIL_SMTP_PASS, MAIL_TIMEOUT_MS, MAIL_API_URL, MAIL_API_KEYSetează MAIL_SMTP_HOST pentru SMTP, sau MAIL_API_URL cu MAIL_FROM pentru un releu HTTP, sau niciuna.
SecuritateNODE_ENV, LLMEU_IP_SALT, LLMEU_DEMO_PASSWORD, LLMEU_KEY_ENVNODE_ENV=production marchează cookie-urile de sesiune ca Secure. LLMEU_IP_SALT adaugă sare la IP-ul clientului hashuit și ar trebui schimbat pentru fiecare implementare. LLMEU_KEY_ENV prefixează cheile generate cu llmeu_live_ sau llmeu_test_.
DiverseLOG_LEVELLOG_LEVEL stabilește nivelul minim de logare.

Docker

Compose și imaginea sunt opționale: suita proprie a depozitului nu necesită Docker, iar calea principală este un proces local fără servicii.

  • Profilul implicit rulează web: porturile 3000 și 8088, SQLite pe volumul llmeu-data la /app/data, API-ul încorporat și un healthcheck pe /healthz.
  • Aceeași imagine rulează worker-ul cu node apps/worker/src/run.mjs, așteptând ca web să devină sănătos și partajând volumul llmeu-data.
  • Două profiluri suplimentare sunt dezactivate implicit: postgres (postgres:16-alpine) și redis (redis:7-alpine, rezervat și necitit de calea de cod v1).
  • Profilul gpu rulează vllm/vllm-openai cu o rezervare de dispozitiv NVIDIA și montează ponderile read-only de pe gazdă; nu este activat implicit, deoarece a pretinde inferență în UE fără un GPU ar fi fals.
  • Dockerfile este FROM node:24-alpine cu tini, fără pas de build și fără instalare de dependențe, rulează ca utilizatorul node, scrie doar sub /app/data, expune 3000 și 8088 și pornește apps/web/server/main.mjs.

Copii de siguranță

node scripts/backup.mjs fără opțiuni scrie ./backups/llmeu-<stamp>.db. SQLite este copiat cu VACUUM INTO, niciodată cp, deoarece o bază de date WAL activă copiată cu cp poate produce o stare ruptă sau învechită.

node scripts/backup.mjs                                  # ./backups/llmeu-<stamp>.db
node scripts/backup.mjs --out /srv/backups/              # explicit destination
node scripts/backup.mjs --verify ./backups/llmeu-<stamp>.db
node scripts/backup.mjs --restore ./backups/llmeu-<stamp>.db --yes
node scripts/backup.mjs --list

Fiecare copie de siguranță SQLite este verificată înainte ca scriptul să raporteze succes: este restaurată într-o copie temporară, PRAGMA integrity_check trebuie să returneze ok, schema este reaplicată, iar numărătorile de rânduri pentru modele, organizații și utilizatori sunt afișate.

Cu un URL postgres://, scriptul folosește pg_dump --format=custom și necesită pg_dump în PATH; un dump nu poate fi deschis ca bază de date, astfel încât numărătorile sale de rânduri necesită o restaurare de probă.

E-mail

E-mailul tranzacțional — resetarea parolei, verificarea adresei, invitațiile — folosește MAIL_SMTP_* sau MAIL_API_URL cu MAIL_FROM. Transportul este ales o dată la pornire și raportat pe fiecare pagină care are nevoie de e-mail.

O implementare fără niciunul este o stare suportată: înregistrarea, autentificarea, schimbarea parolei, echipele și invitațiile funcționează, dar e-mailurile de resetare a parolei și de verificare nu pot fi procesate acolo. Paginile spun acest lucru, iar node scripts/db.mjs passwd <email> este ruta de recuperare.

Backend-uri de inferență

Un rând de endpoint numește un runtime și o regiune, iar registrul de backend-uri alege adaptorul în funcție de regiune, astfel încât un endpoint poate fi răspuns doar de o mașină din regiunea pe care o numește.

  • INFERENCE_MOCK=true păstrează backend-ul mock determinist, care nu are nevoie de GPU și nici de ponderi.
  • GPU_ENABLED=false exclude backend-urile administrate; cu GPU_ENABLED=true, VLLM_BASE_URL se aplică singurei regiuni numite de VLLM_REGION.
  • O implementare cu mai multe regiuni setează o variabilă pentru fiecare regiune, numită după slug-ul regiunii (de exemplu VLLM_BASE_URL_DE_FRA). Un endpoint dintr-o regiune fără backend configurat este refuzat, în loc să fie servit de o mașină din altă regiune.
  • Backend-urile partenere provin din PARTNER_BASE_URL_<REGION> și folosesc PARTNER_SOVEREIGNTY și PARTNER_API_KEY; serverul permite utilizarea partenerilor când PARTNER_BASE_URL este setat.

Fără GPU, răspunde backend-ul mock. Regiunea sa este dev-mock, iar ieșirea sa spune că este backend-ul mock, că nu au rulat ponderi reale și că inferența first-party are nevoie de VLLM_BASE_URL și de flagul gpu_enabled. Un succes mock care ar părea real ar fi singurul lucru pe care acest produs nu trebuie să îl livreze niciodată.