Cada ajuste de los tres contenedores de openplate es una variable de entorno. Esta página los lista todos, para la aplicación, el servidor central (openplate-core) y el servicio de inferencia. La mayoría son opcionales. Cuando dejas uno sin definir, se aplica el valor predeterminado de su fila.
Algunos ajustes detienen el arranque a propósito. Un valor que el servicio no pueda usar, o dejar incompleto un par de variables, hace que el contenedor se cierre. El mensaje de salida indica el nombre de la variable. El contenedor no se inicia haciendo suposiciones. Ajustes que detienen el arranque lista cada una de estas reglas.
Cómo definir una variable
La columna Predeterminado indica qué hace el servicio si la variable no está definida. Un archivo compose puede pasar un valor propio, por ejemplo MODEL_PROFILE: lite. El archivo compose muestra ese valor junto al nombre.
Nombres que los archivos compose completan por ti
Los tres archivos de topología, compose.core.yml, compose.inference.yml y compose.full.yml, completan algunas variables del contenedor a partir de nombres compartidos en .env. Define allí el nombre compartido. La variable del contenedor por sí sola no tiene efecto en estos archivos.
En .env | Completa |
|---|
PUBLIC_APP_URL | el APP_URL de la aplicación, y el CLIENT_BASE_URL del servidor central |
PUBLIC_SYNC_URL | el CORE_URL de la aplicación, y el SERVER_PUBLIC_URL del servidor central |
PUBLIC_INFERENCE_URL | DEFAULT_INFERENCE_BASE_URL de la app |
INFERENCE_API_KEY | DEFAULT_INFERENCE_API_KEY de la app, y API_KEYS del servicio de inferencia |
POSTGRES_USER, POSTGRES_PASSWORD, SYNC_DB_NAME | la base de datos, y el DATABASE_URL del servidor central |
docker/compose.yml, la aplicación por sí sola, toma APP_URL bajo su propio nombre. El archivo de inicio rápido propio del servidor central es apps/core/docker/compose.yml. Toma SERVER_PUBLIC_URL y CLIENT_BASE_URL bajo sus propios nombres. Construye DATABASE_URL a partir de POSTGRES_USER, POSTGRES_PASSWORD y POSTGRES_DB.
La app
El contenedor de la app, ghcr.io/lowcarbcheck/openplate. Se inicia sin nada configurado. configuration.md explica a fondo sus funciones principales.
Servidor y direcciones
| Variable | Por defecto | Qué hace | Más información |
|---|
NODE_ENV | production en la imagen; de lo contrario, development | production sirve la app compilada, hace que APP_URL sea obligatoria y establece 1 como el valor predeterminado de TRUST_PROXY. La imagen lo define. | |
APP_URL | http://localhost:3000, obligatorios en producción | La dirección pública que la gente abre, por ejemplo https://openplate.example.com. La página principal la incluye en sus enlaces para compartir. El servidor no se inicia en producción sin ella. | Caddy |
PORT | 3000 | El puerto en el que escucha el servidor. | |
HOST | sin definir, todas las interfaces | La dirección en la que escucha el servidor. Déjala sin definir en un contenedor. Sin un contenedor, 127.0.0.1 mantiene el servidor en esta máquina, para un proxy en el mismo equipo. | Sin Docker |
TRUST_PROXY | 1 en producción, si no, desactivado | Cuántos proxies inversos hay delante de la aplicación: un número, true, false, o un preajuste de Express o rango de direcciones como loopback o 10.0.0.0/8. La comprobación que detiene los envíos de formularios entre sitios necesita el valor correcto detrás de un proxy. Usa 0 si no hay proxy. | La aplicación por sí sola |
CSP_CONNECT_EXTRA | sin definir | Orígenes adicionales para la directiva connect-src de Content-Security-Policy, separados por espacios. Tu propio endpoint de IA en otro host necesita esto. | Endpoints de IA personalizados |
Idioma y contenido
| Variable | Por defecto | Qué hace | Más información |
|---|
DEFAULT_UI_LANGUAGE | en | El idioma que ve quien visita el sitio antes de elegir uno: en, de, fr, it, es o tr. La elección que haga la persona siempre tiene preferencia. Cualquier otro valor detiene el arranque. | |
NUTRIENT_REFERENCE_BASIS | dge | Los valores de referencia que cita la pantalla Nutrientes: dge (DGE de Alemania), efsa (UE) o us (NASEM). Cualquier otro valor detiene el arranque. | |
CONTENT_DIR | sin definir, sin páginas legales | Una carpeta de archivos Markdown para las páginas legales, montada como solo lectura. Un valor que no apunte a una carpeta detiene el arranque. | Páginas de contenido |
Sincronización y tipo de instancia
| Variable | Por defecto | Qué hace | Más información |
|---|
CORE_URL | sin definir, sincronización desactivada | La dirección de tu servidor central, tal como accede a ella un navegador. Su origen va a la Content-Security-Policy. En una instancia administrada, el servidor de la aplicación también accede a ella, para comprobar la cuenta de cada consulta de alimentos. Un valor con formato incorrecto detiene el arranque. | Sincronización |
SYNC_SERVER_URL | sin definir | En desuso. El nombre antiguo de CORE_URL. Sigue funcionando durante una versión más, y el arranque registra una advertencia cuando es el único configurado. Si ambos se definen con direcciones distintas, el nombre antiguo prevalece para esta versión y el arranque registra una advertencia que nombra a los dos. Elimina la línea antigua antes de la versión que retire el nombre antiguo. | Sincronización |
INSTANCE_MODE | open | open o managed. En una instancia administrada, un administrador invita a usuarios, y el servidor central proporciona la IA. managed necesita CORE_URL. Cualquier otro valor detiene el arranque. | Instancias gestionadas |
IA provista por la instancia
| Variable | Por defecto | Qué hace | Más información |
|---|
DEFAULT_INFERENCE_BASE_URL | sin definir | Un endpoint compatible con OpenAI que esta instancia ofrece a cada visitante, tal como accede a él un navegador. Un valor con formato incorrecto detiene el arranque. | IA provista por la instancia |
DEFAULT_INFERENCE_API_KEY | sin definir | La clave para ese endpoint. Es pública: la recibe el navegador de cada visitante. | IA provista por la instancia |
DEFAULT_INFERENCE_MODEL | openplate-plate-1 | El nombre del modelo que se envía a ese endpoint. | IA provista por la instancia |
Base de datos de alimentos
| Variable | Por defecto | Qué hace | Más información |
|---|
FOOD_DB_API_URL | https://lowcarbcheck.org | La base de datos de alimentos de LowCarbCheck en la que el servidor busca los nombres de los alimentos. Un valor vacío desactiva la búsqueda. | La clave de la base de datos de alimentos |
FOOD_DB_API_KEY | sin definir, el nivel anónimo | Tu clave de LowCarbCheck. Solo la lee el servidor y nunca llega a un navegador. | La clave de la base de datos de alimentos |
FOOD_DB_BACKFILL | false | true envía a LowCarbCheck como propuestas los alimentos que los usuarios guardan a partir de una respuesta de la IA. Necesita FOOD_DB_API_KEY y permanece desactivado sin ella. Cualquier valor distinto de true o false detiene el arranque. | Propuestas a la base de datos de alimentos |
FOOD_DB_DAILY_CALL_LIMIT | 3200 | El máximo de llamadas a LowCarbCheck que este servidor realiza en un día UTC. Al superarlo, las búsquedas de alimentos se pausan hasta la medianoche UTC y la app muestra un aviso. El valor por defecto se ajusta a las 100 000 mensuales de una clave gratuita. Cualquier valor que no sea un número entero positivo detiene el arranque. | La clave de la base de datos de alimentos |
Analítica, boletín y actualizaciones
| Variable | Por defecto | Qué hace | Más información |
|---|
MATOMO_URL | sin definir, analítica desactivada | Una instalación de Matomo que administres tú. Configúrala junto con MATOMO_SITE_ID. | Analítica |
MATOMO_SITE_ID | sin definir | El id de sitio de Matomo, un número entero positivo. Configúralo junto con MATOMO_URL. | Analítica |
MATOMO_EVENT_LEVEL | product | Cuánto cuenta la instancia: pageviews, product o research. Necesita los dos ajustes anteriores. | Qué determina un nivel |
NEWSLETTER_SUBSCRIBE_URL | sin definir, sin formulario | A dónde reenvía el servidor el formulario del boletín de la página de inicio. Configúralo junto con NEWSLETTER_TURNSTILE_SITE_KEY. | Suscripción al boletín |
NEWSLETTER_TURNSTILE_SITE_KEY | sin definir | La clave de sitio de Cloudflare Turnstile de ese formulario. No es el captcha de registro, consulta Registro con Turnstile. | Suscripción al boletín |
UPDATE_CHECK | activado | Si configuras esto en off o false, el servidor dejará de consultar openplate.de/latest.json para buscar nuevas versiones. También detendrá el recuento diario de peticiones del proyecto. | La comprobación de nuevas versiones |
Registro de eventos
| Variable | Por defecto | Qué hace | Más información |
|---|
LOG_LEVEL | info | El nivel de detalle con el que registra el servidor, como nivel de pino: debug, info, warn o error. | |
Cerrar una instancia
| Variable | Por defecto | Qué hace | Más información |
|---|
MOVED_TO_URL | sin definir | Cierra esta instancia y redirige a los usuarios a otra, por ejemplo https://app.openplate.de. Cada solicitud de página sirve una página que indica la nueva dirección, el service worker instalado en los teléfonos borra sus cachés y anula su registro, y la API devuelve 410. El valor debe ser una dirección https:// en un host distinto de APP_URL; cualquier otra cosa detiene el arranque. | Trasladar usuarios a otra instancia |
El servidor central (openplate-core)
El contenedor del servidor central, ghcr.io/lowcarbcheck/openplate-core. Necesita dos valores: DATABASE_URL, que los archivos de compose rellenan por ti, y SERVER_SECRET. Todo lo demás es opcional y está desactivado hasta que lo definas. el README de openplate-core explica las funciones.
Servidor y direcciones
| Variable | Por defecto | Qué hace | Más información |
|---|
NODE_ENV | production en la imagen | Si configuras el correo, production exige que ambas direcciones de enlace a continuación sean direcciones https:// en otro host. | El correo necesita las direcciones públicas |
PORT | 3000 | El puerto en el que escucha el servicio. | |
HOST | sin definir, todas las interfaces | La dirección en la que escucha el servicio. No la definas dentro de un contenedor. 127.0.0.1 mantiene una instancia de desarrollo en su propia máquina. | |
TRUST_PROXY | false | Cuántos proxies inversos hay delante: un número, true o false. Con un valor incorrecto detrás de un proxy, todas las solicitudes parecerán proceder del proxy. Una sola persona podría agotar entonces el límite de todos los demás. | Tres ajustes importantes |
SERVER_PUBLIC_URL | sin definir | La dirección pública propia de este servicio. Se incluye en los enlaces de los correos de invitación y de restablecimiento de contraseña. Configúrala junto con CLIENT_BASE_URL. | El correo necesita las direcciones públicas |
CLIENT_BASE_URL | sin definir | La dirección de la app openplate, la otra mitad de esos enlaces. | El correo necesita las direcciones públicas |
INSTANCE_NAME | openplate | Establece el nombre de la instancia para la negociación /health (instance.name) y el registro de inicio. Los correos no lo usan. Máximo 64 caracteres. | |
INSTANCE_LANGUAGE | en | Define el idioma de los correos si una solicitud no especifica ninguno. Los valores aceptados son en, de, fr, it, es o tr. Cualquier otro valor detiene el arranque. | |
NUTRIENT_REFERENCE_BASIS | dge | Una instancia nueva arranca con uno de estos valores de referencia: dge, efsa o us. Quien administre el sistema puede cambiar este ajuste en vivo más adelante mediante la API de administración. Cualquier otro valor detiene el arranque. | |
CONTENT_DIR | sin definir | Una carpeta montada en solo lectura con el texto de las cartas de declaración. La app puede leer sus páginas legales desde esta carpeta. El servicio nunca la comprueba al arrancar. | Cartas de declaración |
LEGAL_DECLARATION_RECEIPTS_PER_DAY | 200 | El máximo de acuses de recibo de declaraciones que la instancia envía por correo en un periodo de 24 horas, sumando todas las direcciones. Después de eso, la declaración se sigue registrando y se te envía, pero se omite su acuse de recibo. | Cartas de declaración |
LEGAL_DECLARATION_RECEIPTS_PER_NETWORK_PER_DAY | 10 | El mismo límite para una red emisora, ya sea una dirección IPv4 o una IPv6 /64. Un reinicio restablece este recuento. | Cartas de declaración |
Base de datos
| Variable | Por defecto | Qué hace | Más información |
|---|
DATABASE_URL | ninguno, obligatorios | La cadena de conexión de Postgres. Los archivos compose la generan por ti. | |
DATABASE_SSL | false | Establécelo en true si Postgres requiere TLS. Acepta true, false, 1 o 0. | |
MIGRATIONS_DIR | drizzle/migrations | Indica dónde encuentra el servicio sus migraciones de base de datos al arrancar. La imagen ya las contiene ahí, de modo que no lo definas. | |
Secretos
| Variable | Por defecto | Qué hace | Más información |
|---|
SERVER_SECRET | ninguno, obligatorios | El secreto raíz debe tener al menos 32 caracteres. Genéralo con openssl rand -hex 32 y guárdalo en una copia de seguridad junto con la base de datos. Si cambias este secreto, bloquearás de forma permanente el acceso a todas las cuentas. | Tres ajustes importantes |
ADMIN_TOKEN | sin definir | La credencial de administración del operador debe tener al menos 24 caracteres. La necesitas para crear la primera cuenta. Sin token ni cuenta de administrador, la API de administración devuelve 404. | Crea la primera cuenta |
Cuentas y registro
| Variable | Por defecto | Qué hace | Más información |
|---|
OPEN_SIGNUP | sin definir, solo invitaciones | true permite que cualquiera solicite una cuenta con su propia dirección. Esto requiere correo. El servidor solo acepta true. Cualquier otro valor, incluido false, detiene el arranque. | Registro con Turnstile |
TURNSTILE_SECRET_KEY | sin definir, sin captcha | La clave secreta de Cloudflare Turnstile que verifica el captcha de registro. Defínela junto con TURNSTILE_SITE_KEY, y solo con OPEN_SIGNUP=true. | Registro con Turnstile |
TURNSTILE_SITE_KEY | sin definir | La clave pública de sitio de Turnstile. /health la publica y la app la usa para renderizar el captcha. | Registro con Turnstile |
Invitaciones y periodos de prueba
| Variable | Por defecto | Qué hace | Más información |
|---|
MEMBER_INVITE_DAILY_AI_LIMIT | sin definir, los miembros no pueden invitar | El número de peticiones de IA por día UTC que recibe una cuenta cuando la invita un miembro, de 1 a 10000. Defínelo junto con MEMBER_INVITE_ALLOWANCE_DAYS. | Invitaciones de miembros |
MEMBER_INVITE_ALLOWANCE_DAYS | sin definir | El número de días tras el registro durante los que dura esta cuota. | Invitaciones de miembros |
MEMBER_INVITE_LIFETIME_CAP | 5 | El total de invitaciones que un miembro puede enviar en total: 0 o más. Requiere el par anterior, o MEMBER_INVITE_TRIAL=true. | Invitaciones de miembros |
MEMBER_INVITE_TRIAL | false | true hace que una invitación de miembro otorgue la prueba de escaneos indicada abajo en lugar de la cuota por días. Requiere la prueba y no se puede usar con el par anterior. | |
TRIAL_SCANS | sin definir, sin prueba | Los escaneos de IA gratuitos que recibe una cuenta nueva, de 1 a 100. Defínelo junto con TRIAL_DAILY_AI_LIMIT, y define TRIAL_ADDRESS_PEPPER con ellos. | |
TRIAL_DAILY_AI_LIMIT | sin definir | El número de peticiones de IA por día UTC durante la prueba, de 1 a 10000. | |
TRIAL_DAYS | sin definir, sin fecha de fin | La prueba también termina a medianoche tras este número de días, de 1 a 90, lo que ocurra primero. Requiere el par de la prueba. | |
TRIAL_TIME_ZONE | UTC | La zona horaria de esa medianoche, como nombre IANA como Europe/Berlin. Requiere TRIAL_DAYS. Una zona desconocida detiene el arranque. | |
TRIAL_ADDRESS_PEPPER | sin definir | El secreto que aplica el límite de una prueba por buzón. Debe tener al menos 32 caracteres. Obligatorio con el par de la prueba. Si cambias este valor, se olvidará qué buzones tuvieron una prueba. | |
TRIAL_HASH_RETENTION_DAYS | 365 | Los días que se conserva el hash del buzón de una cuenta eliminada, de 1 a 3650, contados desde la eliminación. Después, un barrido cada hora lo elimina, y el mismo buzón puede volver a tener una prueba. Una instancia sin prueba de escaneo no conserva ningún hash. | |
AI_TRIAL_INSTANCE_DAILY_LIMIT | sin definir, sin límite | El importe total que todas las cuentas de prueba juntas pueden gastar por día UTC. Requiere la prueba. | |
AI_TRIAL_NETWORK_DAILY_LIMIT | una décima parte de AI_TRIAL_INSTANCE_DAILY_LIMIT, al menos 1 | La cantidad del límite de prueba que las peticiones de prueba de una misma red (una IPv6 /64 o una única dirección IPv4) pueden gastar por día UTC. Está desactivado sin AI_TRIAL_INSTANCE_DAILY_LIMIT, y no debe ser superior a este. Los usuarios detrás de un mismo CGNAT IPv4 lo comparten. | |
DEFAULT_FREE_DAILY_AI_LIMIT | sin definir, desactivado | Las solicitudes de IA por día UTC que recibe cada cuenta que no tiene un límite gratuito propio, de 0 a 10000. Nunca termina y no tiene límite de escaneos. Si se agota el día, responde 429 con Retry-After. Sustituye a la prueba de escaneo, por lo que configurarlo junto con el par de prueba detiene el arranque. Una cuenta que todavía tiene una prueba queda bajo este límite. | |
DEFAULT_CAPABILITIES | sin definir, sin comprobación | Lo que puede utilizar una cuenta que no tiene una lista propia de capacidades: etiquetas separadas por comas como scan,recipes, o none para nada. Si no se define o está vacío, el proxy de IA no comprueba ninguna función y cada solicitud pasa. Una solicitud de una función de la que carece la cuenta recibe 403 capability-required. | |
CAPABILITY_SCHEMA_MAP | sin definir, vacío | Pares schemaName:label separados por comas. Una solicitud que pida el esquema de salida estructurada que enumeras necesita esa etiqueta, diga lo que diga su cabecera X-Openplate-Feature. | |
Correo
| Variable | Por defecto | Qué hace | Más información |
|---|
SMTP_HOST | sin definir | El nombre o dirección del servidor SMTP, sin esquema, puerto ni ruta. | SMTP |
SMTP_PORT | 587 | 465 usa TLS desde el primer byte. Cualquier otro puerto debe actualizarse con STARTTLS, salvo un interceptor de correo en esta máquina. | SMTP |
SMTP_USER | sin definir | El nombre de usuario SMTP. Defínelo junto con SMTP_PASSWORD, o deja ambos sin definir para un servidor sin autenticación. | SMTP |
SMTP_PASSWORD | sin definir | La contraseña SMTP. | SMTP |
SMTP_FROM | sin definir | El remitente, con formato address o Name <address>. SMTP lo requiere. | SMTP |
MAIL_API_URL | sin definir | Una API de correo HTTP compatible con Resend. Defínela junto con MAIL_API_KEY, MAIL_API_FROM y MAIL_OPERATOR_EMAIL. | Una API de correo HTTP |
MAIL_API_KEY | sin definir | La clave de la API de correo, enviada como token Bearer. | Una API de correo HTTP |
MAIL_API_FROM | sin definir | La dirección del remitente para la API de correo. | Una API de correo HTTP |
MAIL_OPERATOR_EMAIL | sin definir | Tu propia dirección. Recibe tu copia de una cancelación o de un desistimiento. Ambos transportes la requieren. | SMTP |
NODE_EXTRA_CA_CERTS | sin definir | La ruta dentro del contenedor a un archivo PEM con entidades de certificación adicionales. Node.js lo lee al iniciar. Monta el archivo si usas un relé de correo cuyo certificado esté firmado por una entidad privada. | Un relay con una autoridad de certificación privada |
Proxy de IA y límites
| Variable | Por defecto | Qué hace | Más información |
|---|
UPSTREAM_BASE_URL | sin definir, sin IA | La dirección compatible con OpenAI del proveedor, por ejemplo https://openrouter.ai/api/v1. Defínela junto con UPSTREAM_API_KEY. | Instancias gestionadas |
UPSTREAM_API_KEY | sin definir | La clave del proveedor. Nunca llega al navegador. | Instancias gestionadas |
UPSTREAM_ZDR | sin definir | Solo OpenRouter. Si lo estableces en true, el proxy pide a OpenRouter que enrute una solicitud únicamente a endpoints con retención de datos cero. Cualquier otro host lo ignora. Un valor distinto de true, false o vacío detiene el arranque. | El proxy de IA |
UPSTREAM_PROVIDER_ONLY | sin definir | Solo OpenRouter. Una lista de slugs de proveedores separados por comas, por ejemplo google-vertex. Una solicitud va a uno de ellos o falla, y nunca recurre a otro proveedor. Cualquier otro host lo ignora. | El proxy de IA |
UPSTREAM_TIMEOUT_MS | 120000 | El tiempo en milisegundos que el proxy espera a la primera respuesta del proveedor y, después, entre sus partes. | |
AI_TIERS_FILE | sin definir | De dónde procede el modelo de cada tipo de petición. Si no se define, el modelo es AI_ADVERTISED_MODEL, con el enrutamiento de UPSTREAM_ZDR y UPSTREAM_PROVIDER_ONLY, exactamente igual que antes de que existiera el archivo. bundled usa el archivo de la imagen del núcleo, ai-tiers.json, que reúne el modelo, su enrutamiento y su precio. Una ruta absoluta usa un archivo que montes. Un valor incorrecto o un archivo dañado detiene el arranque. | El proxy de IA |
AI_ADVERTISED_MODEL | sin definir | El modelo que usa cada escaneo cuando no hay un archivo de niveles. /health le da nombre, y el proxy lo incluye en cada petición. La aplicación no escanea sin un modelo. Con un archivo de niveles solo anula el modelo del nivel por defecto, como medida de emergencia, y el núcleo registra una advertencia en cada arranque. | Instancias gestionadas |
AI_MAX_OUTPUT_TOKENS | 8192 | El máximo de tokens de salida que puede pedir una petición. | |
AI_RATE_LIMIT_PER_MINUTE | 20 | El máximo de peticiones que una cuenta puede hacer en una ventana de 60 segundos. | |
AI_INSTANCE_DAILY_LIMIT | sin definir, sin límite | El límite diario de la instancia en unidades de IA por día UTC. El escaneo de una foto del plato cuesta una unidad. | El proxy de IA |
AI_BUDGET_ALERT_FRACTION | 0.2 | En una clave de OpenRouter, envía un correo a MAIL_OPERATOR_EMAIL una vez por período de restablecimiento cuando quede menos de esta fracción del límite de la clave. Debe ser mayor que 0 y menor que 1. | El proxy de IA |
AI_MAX_REQUEST_BYTES | 8000000 | La petición más grande que acepta el proxy, en bytes. | |
AI_MAX_IMAGE_PARTS | 1 | Máximo de imágenes por solicitud. Las solicitudes que superen este límite devuelven 400 ai-request-too-large antes de iniciar el recuento. | |
AI_MAX_TEXT_BYTES | 49152 | Máximo de bytes de texto por solicitud, sumando el texto del mensaje y response_format. Las solicitudes que lo superen devuelven el mismo 400. | |
AI_MAX_MESSAGES | 4 | Máximo de mensajes por solicitud. Las solicitudes que superen este límite devuelven el mismo 400. | |
AI_UNIT_INPUT_TOKENS | 8192 | Tokens de entrada estimados por cada unidad de IA. Cada solicitud cuesta una unidad, más una por cada AI_UNIT_INPUT_TOKENS adicional. Los límites diarios contabilizan estas unidades. | El proxy de IA |
AI_IMAGE_INPUT_TOKENS | 1500 | Tokens de entrada estimados para una imagen. El texto se calcula dividiendo sus bytes totales entre 4. | |
Consentimiento
| Variable | Por defecto | Qué hace | Más información |
|---|
HEALTH_CONSENT_VERSION | sin definir, no se pide consentimiento | La versión del consentimiento sobre datos de salud que debe aceptar cada cuenta, como 2026-09-28. Usa de 1 a 32 letras, dígitos, ., _ o -. Si cambias la versión, se vuelve a pedir a todo el mundo. | Consentimiento explícito |
Push
| Variable | Por defecto | Qué hace | Más información |
|---|
VAPID_PUBLIC_KEY | sin definir, no hay notificaciones | La clave pública para web push. Define las tres o ninguna. Genera un par con pnpm core-api push keygen. | |
VAPID_PRIVATE_KEY | sin definir | La clave privada para web push. | |
VAPID_SUBJECT | sin definir | La forma en que te contacta un servicio push: una dirección mailto: o una dirección https://. | |
PUSH_ENDPOINT_HOSTS | sin definir | Hosts de push adicionales separados por comas que un dispositivo puede registrar. *.example.org cubre todos los hosts bajo example.org. Los servicios push predeterminados del navegador se permiten siempre. Configura esto solo para hosts personalizados. Una entrada mal formada detiene el arranque. | |
Planes
| Variable | Por defecto | Qué hace | Más información |
|---|
PLANS_UPSTREAM_URL | sin definir, no hay planes | La dirección interna del servicio de facturación que recibe /v1/plans/*. Defínela junto con PLANS_UPSTREAM_SECRET. | Planes de pago |
PLANS_UPSTREAM_SECRET | sin definir | El secreto compartido que comprueba el servicio de facturación. | Planes de pago |
BILLING_TOKEN | sin definir | La credencial propia del servicio de facturación, de al menos 24 caracteres. Solo tiene acceso a tres rutas de administración. | Planes de pago |
BILLING_MAX_DAILY_AI_LIMIT | 1000 | Límite diario máximo de IA que BILLING_TOKEN puede asignar a una cuenta. Mantenlo igual o por encima de tu plan más alto. ADMIN_TOKEN no está sujeto a él. | Planes de pago |
Comentarios, uso compartido e investigación
| Variable | Por defecto | Qué hace | Más información |
|---|
SYNC_FEEDBACK | false | true acepta las estimaciones reportadas junto con sus fotos, guardadas durante 30 días. Puedes ver esas fotos. | Estimaciones notificadas |
FEEDBACK_DAILY_LIMIT | 5 | Los informes que puede enviar una cuenta por día UTC. | |
FEEDBACK_MAX_REQUEST_BYTES | 8000000 | El informe más grande que acepta el servicio, en bytes. | |
SYNC_SHARING | false | true activa compartir un diario con un médico. | |
SYNC_RESEARCH | false | true activa las contribuciones para investigación y la consola del estudio. | Sincronización |
Registro y otros
| Variable | Por defecto | Qué hace | Más información |
|---|
LOG_LEVEL | info | debug, info, warn o error. Cualquier otro valor detiene el arranque. | |
SYNC_NOTICE | sin definir | Un mensaje corto que muestra cada aplicación al conectarse, de 280 caracteres como máximo. | |
SYNC_NOTICE_URL | sin definir | Un enlace junto al aviso, https:// o http://. Necesita SYNC_NOTICE. | |
SERVICE_VERSION | sin definir, la versión de la imagen | Sustituye la versión que informa /health. Déjalo sin definir. | |
El servicio de inferencia (openplate-inference)
El contenedor de inferencia, ghcr.io/lowcarbcheck/openplate-inference. Ejecuta el runtime del modelo y el servicio en un único contenedor. Guía de configuración de openplate-inference explica las opciones en detalle.
Servicio y acceso
| Variable | Por defecto | Qué hace | Más información |
|---|
PORT | 8300 | El puerto en el que escucha el servicio. Es el único puerto que publica el contenedor. | |
API_KEYS | sin definir, una clave temporal | Las claves que quien llama debe enviar, separadas por comas. Si no se define, el servicio crea una clave al iniciar, la muestra una vez y la olvida en el siguiente reinicio. | Obtener la clave |
LOG_LEVEL | info | debug, info, warn o error. Cualquier otro valor detiene el arranque. | |
PROFILE | definido a partir de MODEL_PROFILE | El nombre del perfil que muestra el registro de inicio: lite, quality o custom. El contenedor lo establece a partir de MODEL_PROFILE y no cambia nada más. | |
Modelo y pesos
| Variable | Por defecto | Qué hace | Más información |
|---|
MODEL_PROFILE | lite | Los pesos que el contenedor descarga y ejecuta: lite, lite-apache o quality. external no descarga nada y usa tu propio runtime en MODEL_RUNTIME_URL. | Hardware |
MODELS_DIR | /models | Dónde se guardan los pesos en el contenedor, en un volumen. Cámbialo solo si montas los pesos en otro sitio. | |
WEIGHTS_MIRROR_BASE | sin definir | Un servidor espejo desde el que descargar los pesos, con Hugging Face como alternativa. El servicio comprueba las sumas de verificación en ambos casos. | |
MODEL_RUNTIME_URL | http://127.0.0.1:8080, el runtime integrado | La dirección de tu propio runtime, sin /v1. MODEL_PROFILE=external la requiere. Con cualquier otro perfil, una dirección diferente detiene el contenedor. | Variables del modo externo |
MODEL_RUNTIME_API_KEY | sin definir | Una clave que el servicio envía a tu runtime, para un runtime o proxy que la requiera. | Variables del modo externo |
MODEL_ID | openplate-plate-1 | El nombre del modelo que el servicio envía al runtime. vLLM necesita su nombre exacto servido. | Variables del modo externo |
RUNTIME_PORT | 8080 | El puerto del llama-server integrado, solo en la dirección de bucle local del contenedor. | |
CONTEXT_SIZE | 8192 | El contexto para cada escaneo en curso. El contenedor lo multiplica por CONCURRENCY para llama.cpp. | |
LLAMA_THREADS | el número de núcleos menos dos, al menos 1 | Los hilos de CPU para llama.cpp. | |
LLAMA_EXTRA_ARGS | sin definir | Flags adicionales para el final del comando llama-server, separados por espacios. Solo para el runtime integrado. | |
GPU_LAYERS | detectado: 99 con una GPU, de lo contrario 0 | Cuántas capas del modelo van a la GPU. 0 fuerza el uso de la CPU. | |
NVIDIA_VISIBLE_DEVICES | establecido por el runtime de contenedores de NVIDIA | Lo define --gpus all. Cualquier valor distinto de void o none hace que el contenedor use la GPU. No lo configuras tú. | |
Límites
| Variable | Por defecto | Qué hace | Más información |
|---|
CONCURRENCY | 2 | Los análisis en curso a la vez. También define las ranuras de llama.cpp. | |
MAX_QUEUE_DEPTH | 8 | Los análisis que pueden esperar. A partir de aquí, quien llama recibe un 429. | |
RATE_LIMIT_RPM | 60 | Las solicitudes por minuto para cada clave. | |
LATENCY_CEILING_MS | 0, desactivado | El servicio rechaza cualquier análisis que no pueda completar en esta cantidad de milisegundos. | Hardware |
RUNTIME_COMPLETION_TIMEOUT_MS | 600000 | El tiempo máximo que puede durar una llamada al runtime, en milisegundos. 0 desactiva el límite. | |
MAX_IMAGE_BYTES | 8388608 | La foto más grande que acepta el servicio tras decodificarla, en bytes (8 MiB). | |
IMAGE_MAX_LONG_EDGE | 896 | El lado largo al que el servicio reduce cada foto, en píxeles, como mínimo 112. | |
Datos de alimentos
| Variable | Por defecto | Qué hace | Más información |
|---|
FOOD_SOURCE | fdc | De dónde proceden los macros: fdc (el extracto del USDA en la imagen, sin red), off (Open Food Facts), lcc (LowCarbCheck) o none. | Datos de alimentos |
FDC_DATASET_PATH | ./data/fdc-foods.json | El extracto del USDA, relativo al directorio de trabajo. | |
OFF_API_URL | https://world.openfoodfacts.org | La dirección de Open Food Facts, leída con FOOD_SOURCE=off. | |
LCC_API_URL | https://lowcarbcheck.org | La dirección de LowCarbCheck, leída con FOOD_SOURCE=lcc. | |
LCC_API_KEY | sin definir, el nivel anónimo | Tu clave de LowCarbCheck, leída con FOOD_SOURCE=lcc. | |
EMBEDDING_RUNTIME_URL | sin definir | Un runtime compatible con OpenAI que sirve /v1/embeddings, para mejorar la correspondencia de alimentos. | |
EMBEDDING_RUNTIME_API_KEY | sin definir | La clave para ese runtime. | |
Ajustes que detienen el arranque
Un contenedor que no arranca se nota de inmediato y solo cuesta un reinicio. Un ajuste que se ignora en silencio te hace creer que algo funciona cuando no es así. Por eso, cada regla de abajo detiene el arranque y el registro indica la variable.
Nombres rechazados
Estos nombres fueron ajustes en su momento. Ahora el servicio rechaza arrancar mientras alguno esté definido, e indica qué usar en su lugar. El servidor central los rechaza incluso con un valor vacío, así que elimina la línea. La aplicación rechaza GATEWAY_URL solo cuando tiene un valor.
| Variable | Rechazado por | Usa en su lugar |
|---|
GATEWAY_URL | la app | INSTANCE_MODE=managed. El servidor central asumió el control del proxy de IA. |
SIGNUP_MODE | el servidor central | Nada. Las cuentas proceden de invitaciones y OPEN_SIGNUP=true permite pedir una. |
SIGNUPS_OPEN | el servidor central | Nada, por el mismo motivo. |
REQUIRE_EMAIL_VERIFICATION | el servidor central | Nada. La invitación sirve como verificación de la dirección. |
EMAIL_FROM | el servidor central | MAIL_API_FROM, o SMTP_FROM. |
SMTP_SECURE | el servidor central | Nada. SMTP_PORT decide el cifrado. |
PIGEON_API_KEY | el servidor central | MAIL_API_KEY, o SMTP_USER y SMTP_PASSWORD. |
PIGEON_BASE_URL | el servidor central | MAIL_API_URL, o SMTP_HOST. |
Reglas entre variables
La app.
MATOMO_URL y MATOMO_SITE_ID: define ambas o ninguna. MATOMO_EVENT_LEVEL necesita ambas.
NEWSLETTER_SUBSCRIBE_URL y NEWSLETTER_TURNSTILE_SITE_KEY: define ambas o ninguna.
INSTANCE_MODE=managed necesita CORE_URL.
CORE_URL y el obsoleto SYNC_SERVER_URL: define uno. Si ambos se definen con direcciones distintas, SYNC_SERVER_URL prevalece para esta versión y el arranque registra una advertencia.
APP_URL es obligatorio cuando NODE_ENV=production.
MOVED_TO_URL debe ser una dirección https:// en un host distinto de APP_URL, sin nombre de usuario ni contraseña.
Un valor ajeno a su lista detiene el arranque: DEFAULT_UI_LANGUAGE, NUTRIENT_REFERENCE_BASIS, INSTANCE_MODE, MATOMO_EVENT_LEVEL y FOOD_DB_BACKFILL. También lo detiene un FOOD_DB_DAILY_CALL_LIMIT que no sea un número entero positivo. Direcciones mal formadas en CORE_URL, DEFAULT_INFERENCE_BASE_URL, MATOMO_URL o NEWSLETTER_SUBSCRIBE_URL también impiden arrancar. El arranque se detiene asimismo si MATOMO_SITE_ID no es un número entero positivo o si CONTENT_DIR no es una carpeta.
El servidor central.
DATABASE_URL y SERVER_SECRET son obligatorios.
Longitudes mínimas: 32 caracteres para SERVER_SECRET y TRIAL_ADDRESS_PEPPER, 24 para ADMIN_TOKEN y BILLING_TOKEN.
El correo utiliza un solo transporte. La API HTTP de correo necesita MAIL_API_URL, MAIL_API_KEY, MAIL_API_FROM y MAIL_OPERATOR_EMAIL, los cuatro. SMTP necesita SMTP_HOST, SMTP_FROM y MAIL_OPERATOR_EMAIL, y SMTP_USER y SMTP_PASSWORD van juntos. Las variables de ambos transportes a la vez detienen el arranque, al igual que MAIL_OPERATOR_EMAIL sin ningún transporte.
El correo necesita SERVER_PUBLIC_URL y CLIENT_BASE_URL. Con NODE_ENV=production, ambas deben ser direcciones https:// en otro host, no localhost.
Define ambas o ninguna: UPSTREAM_BASE_URL y UPSTREAM_API_KEY; PLANS_UPSTREAM_URL y PLANS_UPSTREAM_SECRET; TURNSTILE_SECRET_KEY y TURNSTILE_SITE_KEY; MEMBER_INVITE_DAILY_AI_LIMIT y MEMBER_INVITE_ALLOWANCE_DAYS; TRIAL_SCANS y TRIAL_DAILY_AI_LIMIT.
Define las tres o ninguna: VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY y VAPID_SUBJECT.
X requiere Y: OPEN_SIGNUP=true requiere correo. El par de Turnstile requiere OPEN_SIGNUP=true. MEMBER_INVITE_LIFETIME_CAP requiere el par de invitación a miembros o MEMBER_INVITE_TRIAL=true. MEMBER_INVITE_TRIAL=true requiere el par de prueba y rechaza el par de invitación a miembros. El par de prueba requiere TRIAL_ADDRESS_PEPPER. TRIAL_DAYS y AI_TRIAL_INSTANCE_DAILY_LIMIT requieren el par de prueba. AI_TRIAL_NETWORK_DAILY_LIMIT requiere AI_TRIAL_INSTANCE_DAILY_LIMIT. TRIAL_TIME_ZONE requiere TRIAL_DAYS. SYNC_NOTICE_URL requiere SYNC_NOTICE.
El cero se rechaza donde se leería como "desactivado" y significaría lo contrario: AI_INSTANCE_DAILY_LIMIT, AI_TRIAL_INSTANCE_DAILY_LIMIT, AI_TRIAL_NETWORK_DAILY_LIMIT, TRIAL_SCANS, TRIAL_DAILY_AI_LIMIT, TRIAL_DAYS, MEMBER_INVITE_DAILY_AI_LIMIT y MEMBER_INVITE_ALLOWANCE_DAYS. Cualquier otro número debe ser positivo también, salvo dos que aceptan 0: TRUST_PROXY y MEMBER_INVITE_LIFETIME_CAP, donde 0 deja a los miembros sin nada que enviar.
Límites máximos: TRIAL_SCANS 100, TRIAL_DAYS 90, TRIAL_DAILY_AI_LIMIT y MEMBER_INVITE_DAILY_AI_LIMIT 10000, SYNC_NOTICE 280 caracteres, INSTANCE_NAME 64 caracteres.
SYNC_SHARING, SYNC_RESEARCH, SYNC_FEEDBACK, DATABASE_SSL y MEMBER_INVITE_TRIAL admiten true, false, 1 o 0. OPEN_SIGNUP solo admite true.
Un valor fuera de su lista detiene el arranque: INSTANCE_LANGUAGE, NUTRIENT_REFERENCE_BASIS, LOG_LEVEL, TRIAL_TIME_ZONE y HEALTH_CONSENT_VERSION. Un VAPID_SUBJECT que no sea mailto: o https: detiene el arranque. También lo hace un SMTP_HOST con esquema, puerto o ruta. Las direcciones con formato incorrecto en SERVER_PUBLIC_URL, CLIENT_BASE_URL, UPSTREAM_BASE_URL, PLANS_UPSTREAM_URL o SYNC_NOTICE_URL también detienen el arranque.
El servicio de inferencia.
MODEL_PROFILE=external requiere MODEL_RUNTIME_URL. Con cualquier otro perfil, un MODEL_RUNTIME_URL distinto de la dirección integrada detiene el contenedor.
Un MODEL_PROFILE, FOOD_SOURCE, PROFILE o LOG_LEVEL fuera de su lista detiene el arranque. Un MODEL_RUNTIME_URL o EMBEDDING_RUNTIME_URL que no sea una dirección http:// o https:// también detiene el arranque.
Los recuentos y tamaños deben ser números enteros positivos. LATENCY_CEILING_MS y RUNTIME_COMPLETION_TIMEOUT_MS también admiten 0. IMAGE_MAX_LONG_EDGE debe ser de al menos 112, y PORT como máximo 65535.
Registro con Turnstile
De forma predeterminada, una cuenta solo se obtiene mediante una invitación. Define OPEN_SIGNUP=true en el servidor central, y cualquiera podrá solicitar una cuenta con su propia dirección. Entonces, el servicio envía una invitación por correo a esa dirección, y el mensaje demuestra que la dirección funciona. Por tanto, el registro abierto necesita correo, y el servicio no arranca sin él. La aplicación muestra el formulario de registro solo cuando el servidor central indica que su acceso está abierto.
Un captcha evita que los scripts saturen el servicio a peticiones. El servidor central comprueba un captcha de Cloudflare Turnstile cuando configuras dos claves:
- 01
Inicia sesión en el panel de Cloudflare, abre Turnstile y elige Add widget. Una cuenta gratuita es suficiente. No es necesario que tu dominio use Cloudflare.
- 02
Pon un nombre al widget, añade el nombre de host de tu app, como openplate.example.com, y mantén el modo Gestionado. Elige Crear.
- 03
Define la clave de sitio en TURNSTILE_SITE_KEY y la clave secreta en TURNSTILE_SECRET_KEY en el servidor central. Define ambas claves o ninguna.
- 04
Vuelve a crear el servidor central, por ejemplo con docker compose -f <your file> up -d.
/health publica entonces la clave de sitio. La página de registro de la aplicación muestra el captcha. Su botón de envío permanece inactivo hasta que el usuario lo resuelve. La clave secreta nunca sale del servidor central. El servicio envía a Cloudflare la respuesta del captcha y la clave secreta, no la dirección del visitante.
El servicio comprueba solo la solicitud de registro, POST /v1/auth/signup-request. Iniciar sesión, abrir una invitación y todas las demás rutas no llevan captcha. Cuando el servicio no puede comunicarse con Cloudflare, devuelve 503, y la persona puede intentarlo de nuevo más tarde. La comprobación falla de forma cerrada, por lo que una solicitud sin respuesta nunca se aprueba.
La directiva Content-Security-Policy de la aplicación permite el captcha de Cloudflare solo en una instancia gestionada (INSTANCE_MODE=managed), o cuando el formulario del boletín está activo. En otras instancias, el navegador bloquea el captcha y nadie puede enviar el formulario. Configura la aplicación en modo gestionado antes de activar el captcha.
Sin las dos claves, el registro abierto sigue funcionando. Sus otros límites siguen vigentes. El servicio permite cinco solicitudes por hora desde una misma dirección, y un mensaje al día para un mismo buzón. Bloquea direcciones de servicios de correo desechable conocidos. El servicio registra una advertencia al arrancar.
NEWSLETTER_TURNSTILE_SITE_KEY es un ajuste independiente. Pertenece a la aplicación y protege el formulario del boletín en la página de inicio. Su clave secreta se queda en el servicio que recibe dicho formulario, no en openplate. Suscripción al boletín lo explica.
Compilar una imagen por tu cuenta
Los Dockerfiles aceptan varios argumentos de compilación. No son ajustes para un contenedor en ejecución. OPENPLATE_BUILD_SHA estampa el commit en el paquete de la aplicación. La imagen de inferencia toma BASE_IMAGE, la imagen del servidor de llama.cpp sobre la que se compila. También toma NODE_IMAGE, la imagen de Node.js para la compilación. VITE_ALLOWED_HOSTS se aplica solo al servidor de desarrollo de la aplicación y no tiene hosts predeterminados.