Auto-hébergement
Node et un répertoire inscriptible sont toute l'exigence. Les affirmations ci-dessous ont été lues dans les fichiers nommés à côté ; tout ce qui n'a pas pu être confirmé n'est pas ici.
Points d'entrée
- package.json exige Node >= 22.5. Le stockage SQLite est le module node:sqlite propre à Node.
- Il n'y a aucune liste de dépendances : les applications, paquets, scripts et tests s'exécutent sur la bibliothèque standard de Node, et le Dockerfile n'installe rien.
- Trois processus existent ; chacun ouvre la base de données et exécute la migration au démarrage.
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
Datastore
DATABASE_URL=sqlite:./data/llmeu.db ne nécessite aucun serveur, et le répertoire et le fichier sont créés à la première ouverture. Le handle fonctionne en mode WAL avec les clés étrangères activées et un délai d'attente occupé de 5 s.
packages/shared/src/db/schema.sql est le schéma. Il est en création seule et idempotent, donc le réexécuter est sans danger ; une colonne ajoutée à une table existante doit aussi être déclarée dans l'étape additive dans packages/shared/src/db/index.mjs.
Une URL postgres:// emprunte une branche d'adaptateur séparée. Cette branche nécessite le paquet pg, qui n'est pas une dépendance déclarée, et l'en-tête de schema.sql indique de traiter le chemin Postgres comme non vérifié jusqu'à ce qu'un vrai pilote et une migration testée existent. SQLite est le chemin que ce dépôt exécute.
CLI opérateur
node scripts/db.mjs lit DATABASE_URL et ne nécessite aucun service. Commandes :
| Commande | Ce qu'il fait |
|---|---|
| node scripts/db.mjs migrate | Applique schema.sql et rapporte le nombre d'instructions. |
| node scripts/db.mjs seed | Insère ou rafraîchit le catalogue et l'organisation de démonstration, et écrit la clé de démonstration dans data/demo-key.txt avec le mode 0600. |
| node scripts/db.mjs reset | Pour SQLite uniquement : supprime le fichier de base de données et ses compagnons -wal et -shm, puis migre et initialise. Sur Postgres, il refuse. |
| node scripts/db.mjs status | Affiche le moteur, le nombre de tables et le nombre de lignes de chaque table non vide. |
| node scripts/db.mjs demo-key | Émet une nouvelle clé pour l'organisation de démonstration et l'affiche une seule fois. |
| node scripts/db.mjs passwd <email> | Définit le mot de passe d'un utilisateur depuis le serveur et supprime toutes les sessions de cet utilisateur. C'est la voie de récupération lorsqu'aucun transport de messagerie n'est configuré. Sans --password, il en génère un et l'affiche une seule fois. |
Environnement
Regroupés comme .env.example les regroupe.
| Groupe | Variables | Notes |
|---|---|---|
| URLs et ports | APP_URL, API_URL, WEB_PORT, API_PORT, API_EMBED, PUBLIC_API_URL | Le port de l'API est par défaut 8088, pas 8080. API_EMBED=false est pour un déploiement qui exécute l'API comme son propre processus ; PUBLIC_API_URL est la base affichée dans les extraits de documentation. |
| Datastore | DATABASE_URL, REDIS_URL | DATABASE_URL sélectionne le stockage ; REDIS_URL est réservé à la file d'attente des tâches et n'est pas lu par le chemin de code v1. |
| Inférence | INFERENCE_MOCK, GPU_ENABLED, VLLM_BASE_URL, VLLM_REGION, VLLM_MODEL_VERSION, VLLM_BASE_URL_<REGION>, DEFAULT_REGION, PARTNER_* | Voir la section inférence ci-dessous ; PARTNER_* couvre l'URL de base du partenaire, la région, la classe de souveraineté et la clé API. |
| Facturation | FX_EUR_USD | Le grand livre est tenu en micro-USD et l'EUR est dérivé pour l'affichage à FX_EUR_USD. |
| Courriel | 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 | Définissez MAIL_SMTP_HOST pour SMTP, ou MAIL_API_URL avec MAIL_FROM pour un relais HTTP, ou aucun des deux. |
| Sécurité | NODE_ENV, LLMEU_IP_SALT, LLMEU_DEMO_PASSWORD, LLMEU_KEY_ENV | NODE_ENV=production marque les cookies de session comme Secure. LLMEU_IP_SALT sale l'IP client hachée et doit être changé à chaque déploiement. LLMEU_KEY_ENV préfixe les clés générées par llmeu_live_ ou llmeu_test_. |
| Divers | LOG_LEVEL | LOG_LEVEL définit le niveau de journalisation minimum. |
Docker
Compose et l'image sont facultatifs : la suite propre au dépôt ne nécessite pas Docker, et le chemin principal est un processus local sans services.
- Le profil par défaut exécute web : ports 3000 et 8088, SQLite sur le volume llmeu-data à /app/data, l'API embarquée, et un healthcheck sur /healthz.
- La même image exécute le worker avec node apps/worker/src/run.mjs, en attendant que web devienne sain et en partageant le volume llmeu-data.
- Deux profils supplémentaires sont désactivés par défaut : postgres (postgres:16-alpine) et redis (redis:7-alpine, réservé et non lu par le chemin de code v1).
- Le profil gpu exécute vllm/vllm-openai avec une réservation de périphérique NVIDIA et monte les poids en lecture seule depuis l'hôte ; il n'est pas activé par défaut, car revendiquer une inférence UE sans GPU serait faux.
- Le Dockerfile est FROM node:24-alpine avec tini, sans étape de build ni installation de dépendances, s'exécute en tant qu'utilisateur node, écrit uniquement sous /app/data, expose 3000 et 8088, et démarre apps/web/server/main.mjs.
Sauvegardes
node scripts/backup.mjs sans options écrit ./backups/llmeu-<stamp>.db. SQLite est copié avec VACUUM INTO, jamais cp, car une base de données WAL active copiée avec cp peut produire un état déchiré ou obsolète.
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
Chaque sauvegarde SQLite est vérifiée avant que le script ne signale un succès : elle est restaurée dans une copie temporaire, PRAGMA integrity_check doit retourner ok, le schéma est réappliqué, et les nombres de lignes pour les modèles, les organisations et les utilisateurs sont affichés.
Avec une URL postgres://, le script utilise pg_dump --format=custom et nécessite pg_dump dans le PATH ; un dump ne peut pas être ouvert comme une base de données, donc ses nombres de lignes nécessitent une restauration scratch.
Courriel
Le courriel transactionnel — réinitialisation de mot de passe, vérification d'adresse, invitations — utilise MAIL_SMTP_* ou MAIL_API_URL avec MAIL_FROM. Le transport est choisi une fois au démarrage et signalé sur chaque page qui nécessite du courriel.
Un déploiement sans l'un ni l'autre est un état pris en charge : l'inscription, la connexion, le changement de mot de passe, les équipes et les invitations fonctionnent, mais le courriel de réinitialisation de mot de passe et de vérification ne peut pas y être traité. Les pages le disent, et node scripts/db.mjs passwd <email> est la voie de récupération.
Backends d'inférence
Une ligne de point de terminaison nomme un runtime et une région, et le registre de backends choisit l'adaptateur par région, donc un point de terminaison ne peut être traité que par une machine dans la région qu'il nomme.
- INFERENCE_MOCK=true conserve le backend de développement déterministe, qui n'a besoin ni de GPU ni de poids.
- GPU_ENABLED=false exclut les backends gérés ; avec GPU_ENABLED=true, VLLM_BASE_URL s'applique à la seule région nommée par VLLM_REGION.
- Un déploiement avec plusieurs régions définit une variable par région, nommée d'après le slug de la région (par exemple VLLM_BASE_URL_DE_FRA). Un point de terminaison dans une région sans backend configuré est refusé, plutôt que servi depuis une machine dans une autre région.
- Les backends partenaires proviennent de PARTNER_BASE_URL_<REGION> et prennent PARTNER_SOVEREIGNTY et PARTNER_API_KEY ; le serveur autorise l'utilisation de partenaires lorsque PARTNER_BASE_URL est défini.
Sans GPU configuré, le backend de développement déterministe répond et sa sortie indique qu'aucun poids réel n'a été exécuté et que l'inférence first-party nécessite VLLM_BASE_URL et le flag gpu_enabled. Un succès de substitution qui semblerait réel serait la seule chose que ce produit ne doit jamais livrer.