Самостоятелно хостване
Node и директория с права за запис са цялото изискване. Твърденията по-долу са прочетени от файловете, посочени до тях; всичко, което не можа да бъде потвърдено, не е тук.
Входни точки
- package.json изисква Node >= 22.5. SQLite хранилището е собственият модул node:sqlite на Node.
- Изобщо няма списък със зависимости: приложенията, пакетите, скриптовете и тестовете работят със стандартната библиотека на Node, а Dockerfile не инсталира нищо.
- Съществуват три процеса; всеки отваря базата данни и изпълнява миграцията при стартиране.
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
Хранилище за данни
DATABASE_URL=sqlite:./data/llmeu.db не се нуждае от сървър, а директорията и файлът се създават при първо отваряне. Хендълът работи в WAL режим с включени външни ключове и 5 s изчакване при заетост.
packages/shared/src/db/schema.sql е схемата. Тя е само за създаване и идемпотентна, така че повторното ѝ изпълнение е безопасно; колона, добавена към съществуваща таблица, също трябва да бъде декларирана в добавъчната стъпка в packages/shared/src/db/index.mjs.
URL postgres:// поема отделен клон на адаптера. Този клон изисква пакета pg, който не е декларирана зависимост, а заглавната част на schema.sql казва да се третира пътят Postgres като непроверен, докато съществуват реален драйвер и тествана миграция. SQLite е пътят, който това хранилище изпълнява.
CLI за оператор
node scripts/db.mjs чете DATABASE_URL и не се нуждае от услуги. Команди:
| Команда | Какво прави |
|---|---|
| node scripts/db.mjs migrate | Прилага schema.sql и отчита броя на изразите. |
| node scripts/db.mjs seed | Вмъква или опреснява каталога и демонстрационната организация и записва демонстрационния ключ в data/demo-key.txt с права 0600. |
| node scripts/db.mjs reset | Само за SQLite: премахва файла на базата данни и придружаващите го -wal и -shm, след което мигрира и зарежда начални данни. На Postgres отказва. |
| node scripts/db.mjs status | Отпечатва двигателя, броя на таблиците и броя на редовете на всяка непразна таблица. |
| node scripts/db.mjs demo-key | Издава нов ключ за демонстрационната организация и го отпечатва веднъж. |
| node scripts/db.mjs passwd <email> | Задава парола на потребител от сървъра и изтрива всяка сесия на този потребител. Това е пътят за възстановяване, когато не е конфигуриран транспортен механизъм за поща. Без --password генерира такава и я отпечатва веднъж. |
Среда
Групирани както ги групира .env.example.
| Група | Променливи | Бележки |
|---|---|---|
| URL адреси и портове | APP_URL, API_URL, WEB_PORT, API_PORT, API_EMBED, PUBLIC_API_URL | API портът по подразбиране е 8088, не 8080. API_EMBED=false е за внедряване, което пуска API като отделен процес; PUBLIC_API_URL е базата, показвана в примерните фрагменти в документацията. |
| Хранилище на данни | DATABASE_URL, REDIS_URL | DATABASE_URL избира хранилището; REDIS_URL е запазен за опашката от задачи и не се чете от кодовия път на v1. |
| Инференция | INFERENCE_MOCK, GPU_ENABLED, VLLM_BASE_URL, VLLM_REGION, VLLM_MODEL_VERSION, VLLM_BASE_URL_<REGION>, DEFAULT_REGION, PARTNER_* | Вижте раздела за инференция по-долу; PARTNER_* обхваща базовия URL на партньора, региона, класа на суверенитет и API ключа. |
| Фактуриране | FX_EUR_USD, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | Регистърът се поддържа в микро USD и EUR се изчислява за показване при FX_EUR_USD. |
| Поща | MAIL_FROM, MAIL_SMTP_HOST, MAIL_SMTP_PORT, MAIL_SMTP_SECURE, MAIL_SMTP_USER, MAIL_SMTP_PASS, MAIL_TIMEOUT_MS, MAIL_API_URL, MAIL_API_KEY | Задайте MAIL_SMTP_HOST за SMTP, или MAIL_API_URL с MAIL_FROM за HTTP релей, или нито едно от двете. |
| Сигурност | NODE_ENV, LLMEU_IP_SALT, LLMEU_DEMO_PASSWORD, LLMEU_KEY_ENV | NODE_ENV=production маркира сесийните бисквитки като Secure. LLMEU_IP_SALT добавя сол към хеширания IP на клиента и трябва да се сменя при всяко внедряване. LLMEU_KEY_ENV добавя префикс llmeu_live_ или llmeu_test_ към генерираните ключове. |
| Разни | LOG_LEVEL | LOG_LEVEL задава минималното ниво на логване. |
Docker
Compose и изображението са незадължителни: собственият набор от тестове на хранилището не изисква Docker, а основният път е локален процес без услуги.
- Профилът по подразбиране стартира web: портове 3000 и 8088, SQLite на том llmeu-data в /app/data, вграденото API и проверка на състоянието на /healthz.
- Същото изображение стартира worker с node apps/worker/src/run.mjs, изчаквайки web да стане здрав и споделяйки тома llmeu-data.
- Два допълнителни профила са изключени по подразбиране: postgres (postgres:16-alpine) и redis (redis:7-alpine, резервиран и не се чете от кодовия път v1).
- Профилът gpu стартира vllm/vllm-openai с резервация на NVIDIA устройство и монтира тегла само за четене от хоста; не е разрешен по подразбиране, защото твърдение за извод в ЕС без GPU би било невярно.
- Dockerfile е FROM node:24-alpine с tini, без стъпка за изграждане и без инсталиране на зависимости, работи като потребител node, записва само под /app/data, експонира 3000 и 8088 и стартира apps/web/server/main.mjs.
Резервни копия
node scripts/backup.mjs без флагове записва ./backups/llmeu-<stamp>.db. SQLite се копира с VACUUM INTO, никога cp, защото жива WAL база данни, копирана с cp, може да доведе до разкъсано или остаряло състояние.
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
Всяко резервно копие на SQLite се проверява, преди скриптът да отчете успех: възстановява се във временно копие, PRAGMA integrity_check трябва да върне ok, схемата се прилага отново и се отпечатват броят на редовете за модели, организации и потребители.
С URL postgres:// скриптът използва pg_dump --format=custom и се нуждае от pg_dump в PATH; дъмп не може да бъде отворен като база данни, така че броят на редовете му изисква временно възстановяване.
Поща
Транзакционната поща — нулиране на парола, проверка на адрес, покани — използва MAIL_SMTP_* или MAIL_API_URL с MAIL_FROM. Транспортът се избира еднократно при стартиране и се отчита на всяка страница, която се нуждае от поща.
Внедряване без нито едно от двете е поддържано състояние: регистрацията, входът, смяната на парола, екипите и поканите работят, но пощата за нулиране на парола и за проверка не може да бъде обработена там. Страниците го казват, а node scripts/db.mjs passwd <email> е пътят за възстановяване.
Бекендове за инференция
Всеки ред за endpoint посочва среда за изпълнение и регион, а регистърът на бекендовете избира адаптера по регион, така че даден endpoint може да бъде обслужен само от машина в региона, който посочва.
- INFERENCE_MOCK=true запазва детерминистичния mock бекенд, който не се нуждае от GPU и от тегла.
- GPU_ENABLED=false изключва управляваните бекендове; при GPU_ENABLED=true VLLM_BASE_URL се прилага за единствения регион, посочен от VLLM_REGION.
- Внедряване с няколко региона задава по една променлива за регион, именувана по slug на региона (например VLLM_BASE_URL_DE_FRA). Endpoint в регион без конфигуриран бекенд се отказва, вместо да бъде обслужен от машина в друг регион.
- Партньорските бекендове идват от PARTNER_BASE_URL_<REGION> и използват PARTNER_SOVEREIGNTY и PARTNER_API_KEY; сървърът разрешава използване на партньори, когато PARTNER_BASE_URL е зададен.
Без GPU отговаря mock бекендът. Регионът му е dev-mock, а изходът му казва, че е mock бекендът, че не са изпълнявани реални тегла и че инференцията от първа страна изисква VLLM_BASE_URL и флага gpu_enabled. Успех от mock, който изглежда реален, е единственото нещо, което този продукт никога не бива да пуска.