Zelfhosting
Node en een beschrijfbare map vormen de hele vereiste. De onderstaande uitspraken zijn gelezen uit de bestanden die ernaast worden genoemd; alles wat niet kon worden bevestigd, staat hier niet.
Entrypoints
- package.json vereist Node >= 22.5. De SQLite-opslag is Node's eigen node:sqlite-module.
- Er is helemaal geen afhankelijkhedenlijst: apps, packages, scripts en tests draaien op Node's standaardbibliotheek, en de Dockerfile installeert niets.
- Er bestaan drie processen; elk opent de database en voert de migratie uit bij het opstarten.
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 heeft geen server nodig, en de map en het bestand worden bij de eerste opening aangemaakt. De handle draait in WAL-modus met foreign keys aan en een busy timeout van 5 s.
packages/shared/src/db/schema.sql is het schema. Het is alleen voor aanmaken en idempotent, dus opnieuw uitvoeren is veilig; een kolom die aan een bestaande tabel wordt toegevoegd, moet ook worden gedeclareerd in de additieve stap in packages/shared/src/db/index.mjs.
Een postgres://-URL neemt een aparte adapterbranch. Die branch vereist het pg-pakket, dat geen gedeclareerde afhankelijkheid is, en de kop van schema.sql zegt dat het Postgres-pad als niet-geverifieerd moet worden behandeld totdat er een echte driver en een geteste migratie bestaan. SQLite is het pad dat deze repository draait.
Operator-CLI
node scripts/db.mjs leest DATABASE_URL en heeft geen services nodig. Opdrachten:
| Opdracht | Wat het doet |
|---|---|
| node scripts/db.mjs migrate | Past schema.sql toe en rapporteert het aantal statements. |
| node scripts/db.mjs seed | Voegt de catalogus en de demo-organisatie in of ververst deze, en schrijft de demo-sleutel naar data/demo-key.txt met modus 0600. |
| node scripts/db.mjs reset | Alleen voor SQLite: verwijdert het databasebestand en de bijbehorende -wal- en -shm-bestanden, en migreert en seedt daarna. Op Postgres weigert het. |
| node scripts/db.mjs status | Drukt de engine, het aantal tabellen en het rijenaantal van elke niet-lege tabel af. |
| node scripts/db.mjs demo-key | Geeft een nieuwe sleutel uit voor de demo-organisatie en drukt deze eenmalig af. |
| node scripts/db.mjs passwd <email> | Stelt het wachtwoord van een gebruiker in vanaf de server en verwijdert elke sessie van die gebruiker. Dit is het herstelpad wanneer geen mailtransport is geconfigureerd. Zonder --password genereert het er een en drukt deze eenmalig af. |
Omgeving
Gegroepeerd zoals .env.example ze groepeert.
| Groep | Variabelen | Opmerkingen |
|---|---|---|
| URL's en poorten | APP_URL, API_URL, WEB_PORT, API_PORT, API_EMBED, PUBLIC_API_URL | De API-poort is standaard 8088, niet 8080. API_EMBED=false is voor een deployment die de API als eigen proces draait; PUBLIC_API_URL is de basis die in documentatiesnippets wordt getoond. |
| Datastore | DATABASE_URL, REDIS_URL | DATABASE_URL kiest de opslag; REDIS_URL is gereserveerd voor de jobwachtrij en wordt niet gelezen door het v1-codepad. |
| Inferentie | INFERENCE_MOCK, GPU_ENABLED, VLLM_BASE_URL, VLLM_REGION, VLLM_MODEL_VERSION, VLLM_BASE_URL_<REGION>, DEFAULT_REGION, PARTNER_* | Zie de inferentiesectie hieronder; PARTNER_* dekt de partner-basis-URL, regio, soevereiniteitsklasse en API-sleutel. |
| Facturering | FX_EUR_USD | Het grootboek wordt bijgehouden in USD-micro's en EUR wordt voor weergave afgeleid tegen 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 | Stel MAIL_SMTP_HOST in voor SMTP, of MAIL_API_URL met MAIL_FROM voor een HTTP-relay, of geen van beide. | |
| Beveiliging | NODE_ENV, LLMEU_IP_SALT, LLMEU_DEMO_PASSWORD, LLMEU_KEY_ENV | NODE_ENV=production markeert sessiecookies als Secure. LLMEU_IP_SALT zout het gehashte client-IP en moet per deployment worden gewijzigd. LLMEU_KEY_ENV geeft gegenereerde sleutels het voorvoegsel llmeu_live_ of llmeu_test_. |
| Overig | LOG_LEVEL | LOG_LEVEL stelt het minimale logniveau in. |
Docker
Compose en de image zijn optioneel: de eigen suite van de repository vereist geen Docker, en het primaire pad is een lokaal proces zonder services.
- Het standaardprofiel draait web: poorten 3000 en 8088, SQLite op het llmeu-data-volume op /app/data, de ingebedde API, en een healthcheck op /healthz.
- Dezelfde image draait de worker met node apps/worker/src/run.mjs, wacht tot web gezond is en deelt het llmeu-data-volume.
- Twee extra profielen staan standaard uit: postgres (postgres:16-alpine) en redis (redis:7-alpine, gereserveerd en niet gelezen door het v1-codepad).
- Het gpu-profiel draait vllm/vllm-openai met een NVIDIA-apparaatreservering en mount gewichten alleen-lezen vanaf de host; het is niet standaard ingeschakeld, omdat EU-inference claimen zonder GPU onjuist zou zijn.
- De Dockerfile is FROM node:24-alpine met tini, zonder buildstap en zonder dependency-installatie, draait als de node-gebruiker, schrijft alleen onder /app/data, stelt 3000 en 8088 beschikbaar, en start apps/web/server/main.mjs.
Back-ups
node scripts/backup.mjs zonder flags schrijft ./backups/llmeu-<stamp>.db. SQLite wordt gekopieerd met VACUUM INTO, nooit cp, omdat een levende WAL-database die met cp wordt gekopieerd een beschadigde of verouderde staat kan opleveren.
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
Elke SQLite-back-up wordt geverifieerd voordat het script succes meldt: deze wordt hersteld naar een tijdelijke kopie, PRAGMA integrity_check moet ok teruggeven, het schema wordt opnieuw toegepast, en rijenaantallen voor models, organizations en users worden afgedrukt.
Met een postgres://-URL gebruikt het script pg_dump --format=custom en heeft pg_dump nodig in PATH; een dump kan niet als database worden geopend, dus de rijenaantallen ervan vereisen een scratch-restore.
Transactionele e-mail — wachtwoordreset, adresverificatie, uitnodigingen — gebruikt MAIL_SMTP_* of MAIL_API_URL met MAIL_FROM. Het transport wordt eenmalig bij het opstarten gekozen en gemeld op elke pagina die e-mail nodig heeft.
Een deployment met geen van beide is een ondersteunde toestand: aanmelden, inloggen, wachtwoord wijzigen, teams en uitnodigingen werken, maar wachtwoordreset- en verificatie-e-mail kunnen daar niet worden verwerkt. De pagina's zeggen dat, en node scripts/db.mjs passwd <email> is de herstelroute.
Inferentiebackends
Een endpointrij noemt een runtime en een regio, en het backendregister kiest de adapter op basis van regio, dus een endpoint kan alleen worden beantwoord door een machine in de regio die het noemt.
- INFERENCE_MOCK=true houdt de deterministische ontwikkelbackend, die geen GPU en geen gewichten nodig heeft.
- GPU_ENABLED=false houdt beheerde backends buiten; met GPU_ENABLED=true is VLLM_BASE_URL van toepassing op de enige regio die door VLLM_REGION wordt genoemd.
- Een deployment met meerdere regio's stelt één variabele per regio in, vernoemd naar de regioslug (bijvoorbeeld VLLM_BASE_URL_DE_FRA). Een endpoint in een regio zonder geconfigureerde backend wordt geweigerd, in plaats van te worden bediend vanaf een machine in een andere regio.
- Partnerbackends komen van PARTNER_BASE_URL_<REGION> en gebruiken PARTNER_SOVEREIGNTY en PARTNER_API_KEY; de server staat partnergebruik toe wanneer PARTNER_BASE_URL is ingesteld.
Zonder geconfigureerde GPU antwoordt de deterministische ontwikkelbackend en zijn uitvoer zegt dat er geen echte gewichten zijn gebruikt en dat first-party-inferentie VLLM_BASE_URL en de gpu_enabled-vlag nodig heeft. Een vervangende succesmelding die echt leek, zou het enige zijn dat dit product nooit mag uitbrengen.