Hostowanie własne

Node i katalog z prawem zapisu to całe wymagania. Poniższe stwierdzenia odczytano z plików wskazanych obok nich; wszystko, czego nie można było potwierdzić, nie znajduje się tutaj.

Punkty wejścia

  • package.json wymaga Node >= 22.5. Magazyn SQLite to własny moduł Node node:sqlite.
  • Nie ma żadnej listy zależności: aplikacje, pakiety, skrypty i testy działają na bibliotece standardowej Node, a Dockerfile nic nie instaluje.
  • Istnieją trzy procesy; każdy otwiera bazę danych i uruchamia migrację przy starcie.
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

Magazyn danych

DATABASE_URL=sqlite:./data/llmeu.db nie wymaga serwera, a katalog i plik są tworzone przy pierwszym otwarciu. Uchwyt działa w trybie WAL z włączonymi kluczami obcymi i 5-sekundowym limitem czasu oczekiwania.

packages/shared/src/db/schema.sql to schemat. Jest tylko tworzący i idempotentny, więc ponowne uruchomienie jest bezpieczne; kolumnę dodaną do istniejącej tabeli trzeba też zadeklarować w kroku dodającym w packages/shared/src/db/index.mjs.

Adres URL postgres:// wybiera osobną gałąź adaptera. Ta gałąź wymaga pakietu pg, który nie jest zadeklarowaną zależnością, a nagłówek schema.sql mówi, by traktować ścieżkę Postgres jako niezweryfikowaną, dopóki nie istnieje prawdziwy sterownik i przetestowana migracja. Ścieżką, którą uruchamia to repozytorium, jest SQLite.

CLI operatora

node scripts/db.mjs odczytuje DATABASE_URL i nie wymaga żadnych usług. Polecenia:

Polecenie Co robi
node scripts/db.mjs migrateStosuje schema.sql i podaje liczbę instrukcji.
node scripts/db.mjs seedWstawia lub odświeża katalog i organizację demo oraz zapisuje klucz demo do data/demo-key.txt z uprawnieniami 0600.
node scripts/db.mjs resetTylko dla SQLite: usuwa plik bazy danych oraz towarzyszące pliki -wal i -shm, a następnie wykonuje migrację i wypełnia dane. W przypadku Postgres odmawia.
node scripts/db.mjs statusWypisuje silnik, liczbę tabel i liczbę wierszy każdej niepustej tabeli.
node scripts/db.mjs demo-keyWystawia nowy klucz dla organizacji demo i wypisuje go jednorazowo.
node scripts/db.mjs passwd <email>Ustawia hasło użytkownika z serwera i usuwa wszystkie sesje tego użytkownika. To ścieżka odzyskiwania, gdy nie skonfigurowano transportu poczty. Bez --password generuje hasło i wypisuje je jednorazowo.

Środowisko

Pogrupowane tak, jak grupuje je .env.example.

Grupa Zmienne Uwagi
Adresy URL i portyAPP_URL, API_URL, WEB_PORT, API_PORT, API_EMBED, PUBLIC_API_URLPort API domyślnie wynosi 8088, a nie 8080. API_EMBED=false jest dla wdrożenia, które uruchamia API jako własny proces; PUBLIC_API_URL to bazowy adres pokazywany we fragmentach dokumentacji.
Magazyn danychDATABASE_URL, REDIS_URLDATABASE_URL wybiera magazyn; REDIS_URL jest zarezerwowany dla kolejki zadań i nie jest odczytywany przez ścieżkę kodu v1.
WnioskowanieINFERENCE_MOCK, GPU_ENABLED, VLLM_BASE_URL, VLLM_REGION, VLLM_MODEL_VERSION, VLLM_BASE_URL_<REGION>, DEFAULT_REGION, PARTNER_*Zobacz sekcję wnioskowania poniżej; PARTNER_* obejmuje bazowy URL partnera, region, klasę suwerenności i klucz API.
RozliczeniaFX_EUR_USD, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETRejestr jest prowadzony w mikrodolarach, a EUR jest wyliczane do wyświetlenia według FX_EUR_USD.
PocztaMAIL_FROM, MAIL_SMTP_HOST, MAIL_SMTP_PORT, MAIL_SMTP_SECURE, MAIL_SMTP_USER, MAIL_SMTP_PASS, MAIL_TIMEOUT_MS, MAIL_API_URL, MAIL_API_KEYUstaw MAIL_SMTP_HOST dla SMTP albo MAIL_API_URL z MAIL_FROM dla przekaźnika HTTP, albo żadnego.
BezpieczeństwoNODE_ENV, LLMEU_IP_SALT, LLMEU_DEMO_PASSWORD, LLMEU_KEY_ENVNODE_ENV=production oznacza ciasteczka sesyjne jako Secure. LLMEU_IP_SALT dodaje sól do haszowanego adresu IP klienta i powinien być zmieniany przy każdym wdrożeniu. LLMEU_KEY_ENV poprzedza generowane klucze prefiksem llmeu_live_ lub llmeu_test_.
RóżneLOG_LEVELLOG_LEVEL ustawia minimalny poziom logowania.

Docker

Compose i obraz są opcjonalne: własny zestaw testów repozytorium nie wymaga Dockera, a podstawową ścieżką jest lokalny proces bez żadnych usług.

  • Domyślny profil uruchamia web: porty 3000 i 8088, SQLite na wolumenie llmeu-data pod /app/data, wbudowane API oraz healthcheck na /healthz.
  • Ten sam obraz uruchamia worker poleceniem node apps/worker/src/run.mjs, czekając aż web stanie się zdrowy i współdzieląc wolumen llmeu-data.
  • Dwa dodatkowe profile są domyślnie wyłączone: postgres (postgres:16-alpine) i redis (redis:7-alpine, zarezerwowany i nieodczytywany przez ścieżkę kodu v1).
  • Profil gpu uruchamia vllm/vllm-openai z rezerwacją urządzenia NVIDIA i montuje wagi tylko do odczytu z hosta; nie jest domyślnie włączony, ponieważ twierdzenie o wnioskowaniu w UE bez GPU byłoby nieprawdziwe.
  • Dockerfile to FROM node:24-alpine z tini, bez kroku budowania i bez instalowania zależności, działa jako użytkownik node, zapisuje tylko pod /app/data, wystawia porty 3000 i 8088 oraz uruchamia apps/web/server/main.mjs.

Kopie zapasowe

node scripts/backup.mjs bez flag zapisuje ./backups/llmeu-<stamp>.db. SQLite jest kopiowany za pomocą VACUUM INTO, nigdy przez cp, ponieważ działająca baza WAL skopiowana przez cp może dać stan rozerwany lub nieaktualny.

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

Każda kopia zapasowa SQLite jest weryfikowana, zanim skrypt zgłosi sukces: jest odtwarzana do kopii tymczasowej, PRAGMA integrity_check musi zwrócić ok, schemat jest ponownie stosowany, a liczby wierszy dla models, organizations i users są wypisywane.

Przy adresie URL postgres:// skrypt używa pg_dump --format=custom i wymaga pg_dump w PATH; zrzutu nie można otworzyć jako bazy danych, więc jego liczby wierszy wymagają tymczasowego odtworzenia.

Poczta

Poczta transakcyjna — reset hasła, weryfikacja adresu, zaproszenia — używa MAIL_SMTP_* lub MAIL_API_URL z MAIL_FROM. Transport jest wybierany raz przy starcie i zgłaszany na każdej stronie, która wymaga poczty.

Wdrożenie bez jednego i drugiego jest obsługiwanym stanem: rejestracja, logowanie, zmiana hasła, zespoły i zaproszenia działają, ale poczta do resetu hasła i weryfikacji nie może być tam przetwarzana. Strony to komunikują, a node scripts/db.mjs passwd <email> jest drogą odzyskiwania.

Backendy wnioskowania

Wiersz endpointu wskazuje środowisko uruchomieniowe i region, a rejestr backendów wybiera adapter według regionu, więc endpoint może być obsłużony tylko przez maszynę w regionie, który wskazuje.

  • INFERENCE_MOCK=true utrzymuje deterministyczny backend mock, który nie wymaga GPU ani wag.
  • GPU_ENABLED=false wyklucza zarządzane backendy; przy GPU_ENABLED=true VLLM_BASE_URL dotyczy jednego regionu wskazanego przez VLLM_REGION.
  • Wdrożenie z kilkoma regionami ustawia jedną zmienną na region, nazwaną według sluga regionu (na przykład VLLM_BASE_URL_DE_FRA). Endpoint w regionie bez skonfigurowanego backendu jest odrzucany, a nie obsługiwany przez maszynę w innym regionie.
  • Backendy partnerskie pochodzą z PARTNER_BASE_URL_<REGION> i przyjmują PARTNER_SOVEREIGNTY oraz PARTNER_API_KEY; serwer zezwala na użycie partnera, gdy ustawiono PARTNER_BASE_URL.

Bez GPU odpowiada backend mock. Jego region to dev-mock, a jego dane wyjściowe mówią, że to backend mock, że nie uruchomiono prawdziwych wag i że wnioskowanie własne wymaga VLLM_BASE_URL i flagi gpu_enabled. Sukces mocka, który wyglądałby na prawdziwy, byłby jedyną rzeczą, której ten produkt nigdy nie może trafić do wydania.