Autoalojamiento

Node y un directorio con permiso de escritura son todo el requisito. Las afirmaciones siguientes se leyeron de los archivos que se nombran junto a ellas; lo que no se pudo confirmar no está aquí.

Puntos de entrada

  • package.json requiere Node >= 22.5. El almacén SQLite es el módulo node:sqlite propio de Node.
  • No hay ninguna lista de dependencias: las aplicaciones, los paquetes, los scripts y las pruebas se ejecutan con la biblioteca estándar de Node, y el Dockerfile no instala nada.
  • Existen tres procesos; cada uno abre la base de datos y ejecuta la migración al arrancar.
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

Almacén de datos

DATABASE_URL=sqlite:./data/llmeu.db no necesita servidor, y el directorio y el archivo se crean en la primera apertura. El manejador se ejecuta en modo WAL con claves externas activadas y un tiempo de espera de ocupado de 5 s.

packages/shared/src/db/schema.sql es el esquema. Es de solo creación e idempotente, por lo que volver a ejecutarlo es seguro; una columna añadida a una tabla existente también debe declararse en el paso aditivo en packages/shared/src/db/index.mjs.

Una URL postgres:// toma una rama de adaptador separada. Esa rama requiere el paquete pg, que no es una dependencia declarada, y el encabezado de schema.sql dice que se trate la ruta de Postgres como no verificada hasta que exista un controlador real y una migración probada. SQLite es la ruta que ejecuta este repositorio.

CLI de operador

node scripts/db.mjs lee DATABASE_URL y no necesita servicios. Comandos:

Comando Qué hace
node scripts/db.mjs migrateAplica schema.sql e informa del número de sentencias.
node scripts/db.mjs seedInserta o actualiza el catálogo y la organización de demostración, y escribe la clave de demostración en data/demo-key.txt con modo 0600.
node scripts/db.mjs resetSolo para SQLite: elimina el archivo de base de datos y sus acompañantes -wal y -shm, luego migra y siembra. En Postgres, se rechaza.
node scripts/db.mjs statusImprime el motor, el recuento de tablas y el recuento de filas de cada tabla no vacía.
node scripts/db.mjs demo-keyEmite una clave nueva para la organización de demostración y la imprime una vez.
node scripts/db.mjs passwd <email>Establece la contraseña de un usuario desde el servidor y elimina todas las sesiones de ese usuario. Esta es la ruta de recuperación cuando no hay un transporte de correo configurado. Sin --password, genera una y la imprime una vez.

Entorno

Agrupadas como las agrupa .env.example.

Grupo Variables Notas
URLs y puertosAPP_URL, API_URL, WEB_PORT, API_PORT, API_EMBED, PUBLIC_API_URLEl puerto de la API es 8088 por defecto, no 8080. API_EMBED=false es para un despliegue que ejecuta la API como proceso propio; PUBLIC_API_URL es la base que se muestra en los fragmentos de documentación.
DatastoreDATABASE_URL, REDIS_URLDATABASE_URL selecciona el almacén; REDIS_URL está reservada para la cola de trabajos y la ruta de código de la v1 no la lee.
InferenciaINFERENCE_MOCK, GPU_ENABLED, VLLM_BASE_URL, VLLM_REGION, VLLM_MODEL_VERSION, VLLM_BASE_URL_<REGION>, DEFAULT_REGION, PARTNER_*Véase la sección de inferencia más abajo; PARTNER_* cubre la URL base del partner, la región, la clase de soberanía y la clave de API.
FacturaciónFX_EUR_USD, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETEl libro mayor se mantiene en micros de USD y el EUR se deriva para su visualización a FX_EUR_USD.
CorreoMAIL_FROM, MAIL_SMTP_HOST, MAIL_SMTP_PORT, MAIL_SMTP_SECURE, MAIL_SMTP_USER, MAIL_SMTP_PASS, MAIL_TIMEOUT_MS, MAIL_API_URL, MAIL_API_KEYDefina MAIL_SMTP_HOST para SMTP, o MAIL_API_URL con MAIL_FROM para un relay HTTP, o ninguna de las dos.
SeguridadNODE_ENV, LLMEU_IP_SALT, LLMEU_DEMO_PASSWORD, LLMEU_KEY_ENVNODE_ENV=production marca las cookies de sesión como Secure. LLMEU_IP_SALT pone la sal a la IP de cliente con hash y debe cambiarse en cada despliegue. LLMEU_KEY_ENV prefija las claves generadas con llmeu_live_ o llmeu_test_.
VariosLOG_LEVELLOG_LEVEL fija el nivel mínimo de registro.

Docker

Compose y la imagen son opcionales: la propia suite del repositorio no requiere Docker, y la ruta principal es un proceso local sin servicios.

  • El perfil predeterminado ejecuta web: puertos 3000 y 8088, SQLite en el volumen llmeu-data en /app/data, la API integrada y un healthcheck en /healthz.
  • La misma imagen ejecuta el worker con node apps/worker/src/run.mjs, esperando a que web esté saludable y compartiendo el volumen llmeu-data.
  • Dos perfiles adicionales están desactivados de forma predeterminada: postgres (postgres:16-alpine) y redis (redis:7-alpine, reservado y no leído por la ruta de código v1).
  • El perfil gpu ejecuta vllm/vllm-openai con una reserva de dispositivo NVIDIA y monta los pesos en solo lectura desde el host; no está habilitado de forma predeterminada, porque afirmar inferencia en la UE sin una GPU sería falso.
  • El Dockerfile es FROM node:24-alpine con tini, sin paso de compilación ni instalación de dependencias, se ejecuta como el usuario node, escribe solo bajo /app/data, expone 3000 y 8088, e inicia apps/web/server/main.mjs.

Copias de seguridad

node scripts/backup.mjs sin flags escribe ./backups/llmeu-<stamp>.db. SQLite se copia con VACUUM INTO, nunca con cp, porque una base de datos WAL activa copiada con cp puede producir un estado fragmentado o desactualizado.

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

Cada copia de seguridad de SQLite se verifica antes de que el script informe de éxito: se restaura en una copia temporal, PRAGMA integrity_check debe devolver ok, se vuelve a aplicar el esquema y se imprimen los recuentos de filas de modelos, organizaciones y usuarios.

Con una URL postgres://, el script usa pg_dump --format=custom y necesita pg_dump en el PATH; un volcado no se puede abrir como base de datos, por lo que sus recuentos de filas necesitan una restauración de prueba.

Correo

El correo transaccional —restablecimiento de contraseña, verificación de dirección, invitaciones— usa MAIL_SMTP_* o MAIL_API_URL con MAIL_FROM. El transporte se elige una vez al arrancar y se informa en cada página que necesita correo.

Un despliegue sin ninguno de los dos es un estado admitido: el registro, el inicio de sesión, el cambio de contraseña, los equipos y las invitaciones funcionan, pero el correo de restablecimiento y de verificación no puede procesarse allí. Las páginas lo indican, y node scripts/db.mjs passwd <email> es la vía de recuperación.

Backends de inferencia

Una fila de endpoint nombra un runtime y una región, y el registro de backends elige el adaptador por región, así que un endpoint solo puede ser atendido por una máquina de la región que nombra.

  • INFERENCE_MOCK=true mantiene el backend mock determinista, que no necesita GPU ni pesos.
  • GPU_ENABLED=false deja fuera los backends gestionados; con GPU_ENABLED=true, VLLM_BASE_URL se aplica a la única región nombrada por VLLM_REGION.
  • Un despliegue con varias regiones define una variable por región, nombrada según el slug de la región (por ejemplo VLLM_BASE_URL_DE_FRA). Un endpoint en una región sin backend configurado se rechaza, en lugar de servirse desde una máquina de otra región.
  • Los backends de partner provienen de PARTNER_BASE_URL_<REGION> y toman PARTNER_SOVEREIGNTY y PARTNER_API_KEY; el servidor permite el uso de partner cuando PARTNER_BASE_URL está definida.

Sin GPU responde el backend mock. Su región es dev-mock, y su salida indica que es el backend mock, que no se ejecutó ningún peso real y que la inferencia propia necesita VLLM_BASE_URL y el flag gpu_enabled. Un éxito simulado que pareciera real sería lo único que este producto no debe lanzar nunca.