Volver a la documentación
Guía 03

Configuración

Simmetric Chat tiene dos niveles de configuración: variables de entorno validadas con Zod en el arranque del proceso, y una capa de configuración runtime almacenada en la base de datos que los administradores pueden editar desde la interfaz sin reiniciar…

4 min de lectura

Simmetric Chat tiene dos niveles de configuración: variables de entorno validadas con Zod en el arranque del proceso, y una capa de configuración runtime almacenada en la base de datos que los administradores pueden editar desde la interfaz sin reiniciar ningún servicio.

  1. Variables de entorno (arranque del proceso) - validadas con Zod en packages/<pkg>/src/config/env.ts; process.exit(1) si son inválidas.
  2. Configuración runtime (DB `SystemConfig`) - editable desde la UI de administración sin reiniciar.

Resolución:

  • Claves ALWAYS_READONLY (JWT_SECRET, DATABASE_URL, SERVER_PORT, COLLECTOR_PORT, SERVER_URL, COLLECTOR_URL, LICENSE_SECRET): ENV > Default, no editables desde la UI.
  • Todas las demás: DB > ENV > Default, editables via PUT /api/system/settings.

Los tres paquetes leen .env via path.resolve(__dirname, "../../.env") (independiente del directorio de trabajo). El documento de referencia es .env.example en la raíz del repo.

Variables de entorno - server

Schema: packages/server/src/config/env.ts. Se muestran los valores por defecto; OBL = obligatorio.

Core / infra

VariableDefaultObl.Descripción
NODE_ENVdevelopmentoptdevelopment/production/test
SERVER_PORT3000optPuerto de la API del servidor
COLLECTOR_PORT3210optPuerto del collector (para la URL por defecto)
SERVER_URLhttp://localhost:3000optURL pública del servidor
COLLECTOR_URLhttp://localhost:3210optURL del collector
WIDGET_SERVICE_URLhttp://localhost:3211optURL del servicio widget (push cache-bust)
WIDGET_API_KEY—optSecreto compartido del push widget; ausente → cache-bust no-op (TTL 5 min)
DATABASE_URLpostgresql://simmetricchat:[email protected]:5432/simmetricchatoptURL de PostgreSQL
JWT_SECRET—OBLSecreto JWT
ENCRYPTION_KEY—optBase64 de 32 bytes para AES-256-GCM (data-at-rest). Sin definir → fallback scryptSync(JWT_SECRET, salt)
SESSION_EXPIRY86400000 (24h ms)optCaducidad JWT
ALLOWED_ORIGINShttp://localhost:5173,127.0.0.1:5173,localhost:3000optAllowlist CORS global (CSV). /api/internal/widget excluido
LOG_LEVELinfooptdebug/info/warn/error
ALLOW_REGISTRATIONfalseoptInterruptor de auto-registro
DISABLE_TELEMETRYtrueoptDesactiva la telemetría

Bootstrap admin (seed)

VariableDefaultDescripción
SEED_BOOTSTRAP_ADMINtrueAuto-seed del admin en el primer arranque (transform custom: false/0/no/off/"" = deshabilitado)
SEED_ADMIN_USERNAMEadminUsername del admin bootstrap
SEED_ADMIN_PASSWORDadmin123Contraseña (min 8); cuenta mustChangePassword=true
SEED_ADMIN_EMAIL[email protected]Email del admin bootstrap

LLM

VariableDefaultDescripción
LLM_PROVIDERollamaopenai/anthropic/ollama/openrouter
LLM_MODELgemma4:latestModelo por defecto
LLM_TEMPERATURE0.70–2
LLM_MAX_TOKENS4096Tope de tokens de completación
LLM_TIMEOUT0Timeout de Axios en ms (0 = ninguno, para LLM locales lentos)
LLM_API_KEY / LLM_API_BASE_URL—Overrides genéricos
OPENAI_API_KEY / OPENAI_MODEL—OpenAI
ANTHROPIC_API_KEY / ANTHROPIC_MODEL—Anthropic
OLLAMA_BASE_URLhttp://ollama:11434Endpoint de Ollama
OLLAMA_MODEL / OLLAMA_API_KEY—Override de Ollama
OPENROUTER_API_KEY / OPENROUTER_BASE_URL / OPENROUTER_MODELhttps://openrouter.ai/api / —OpenRouter

Watchdog de agente (prevención de loops/stalls)

VariableDefaultDescripción
AGENT_WALLCLOCK_TIMEOUT_MS600000 (10 min)Límite superior absoluto para una petición ReAct
AGENT_MAX_TOTAL_TOKENS200000Total prompt+completion por petición
AGENT_MAX_CONTEXT_BYTES500000 (500 KB)Tope del tamaño del array de contexto
AGENT_MAX_TOOL_OUTPUT_LENGTH5000Truncado de la salida por skill
AGENT_MAX_SKILL_EXECUTION_MS60000Timeout por skill
AGENT_LOOP_DETECTION_WINDOW3Abortar tras N tool+input idénticos consecutivos
CHAT_MAX_CONCURRENT_PER_USER5Límite de concurrencia de chats por usuario

Embedding / Vector DB / OCR / Synthesis

VariableDefaultDescripción
EMBEDDING_PROVIDERlocallocal/openai/ollama
EMBEDDING_MODELXenova/all-MiniLM-L6-v2Modelo de embedding
EMBEDDING_API_KEY—API key de embedding
VECTOR_DB_PROVIDERlancedblancedb/qdrant
VECTOR_DB_URL / VECTOR_DB_API_KEY—Para Qdrant remoto
OCR_MODELglm-ocr:latestModelo de visión OCR (formato model:version)
OCR_TIMEOUT600000Timeout de visión OCR
SYNTHESIS_LLM_MODELgemma4:latestModelo del pipeline de síntesis
PUPPETEER_EXECUTABLE_PATH—Chromium para export PDF de archivos

Licencia, push, email, collector

VariableDefaultDescripción
LICENSE_KEY—JWT de licencia Enterprise (HS256). Ausente → Community
LICENSE_SECRET—Secreto de verificación JWT (recomendado); fallback a LICENSE_KEY
COLLECTOR_SECRET—OBL — secreto compartido server↔collector
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT—Web Push; sin definir → claves efímeras generadas en dev
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM—SMTP para reset de contraseña + notificaciones de fallo de backup
LEGACY_PREVIOUS_ENCRYPTION_KEYS—CSV base64 32-byte para rotación de claves
Variables en .env.example pero gestionadas como settings DB (no en envSchema): OCR_ENABLED, OCR_PRECHECK_CHARS, STORAGE_PATH (collector), RESET_PASSWORD_STRATEGY.

Variables de entorno - collector

Schema: packages/collector/src/config/env.ts.

VariableDefaultObl.Descripción
COLLECTOR_PORT3210optPuerto HTTP
COLLECTOR_URLhttp://localhost:3210optURL propia
SERVER_URLhttp://localhost:3000optURL del servidor (callback)
EMBEDDING_PROVIDERlocaloptlocal/openai/ollama
EMBEDDING_MODEL—optModelo de embedding
EMBEDDING_API_KEY—optAPI key
VECTOR_DB_PROVIDERlancedboptlancedb/qdrant
VECTOR_DB_URL / VECTOR_DB_API_KEY—optQdrant
OLLAMA_BASE_URLhttp://ollama:11434optOllama
STORAGE_PATH./storageoptAlmacenamiento filesystem
COLLECTOR_SECRET—OBLDebe coincidir con el servidor

Variables de entorno - widget

Schema: packages/widget/src/config/env.ts.

VariableDefaultObl.Descripción
NODE_ENVdevelopmentoptEntorno
WIDGET_PORT3211optPuerto HTTP
SERVER_URLhttp://localhost:3000optURL del servidor
WIDGET_API_KEY—OBLSecreto compartido widget↔server (debe coincidir)
LOG_LEVELinfooptNivel de log
Frontend: sin variables VITE_* ni archivo .env. El frontend lee branding/config desde el servidor (GET /api/system/settings, GET /api/license/info).

Feature flags y licencia

Definidos en packages/shared/src/constants/license.ts (FEATURE_FLAGS + defaults). Verificados en packages/server/src/services/licenseService.ts.

Cómo pasar de Community a Enterprise

  1. Define LICENSE_KEY (JWT HS256) y, recomendado, LICENSE_SECRET.
  2. jwt.verify → payload validado contra licensePayloadSchema (tier, iss, sub, iat, exp, features).
  3. tier=enterprise → arranca desde ENTERPRISE_FEATURE_DEFAULTS + los overrides features del JWT.
  4. Caducado/inválido → fallback Community con warning.

Graceful degradation: getLicenseInfo() comprueba la caducidad en runtime; si Enterprise caduca durante su uso, degrada a Community sin reiniciar. requireFeature(flag) → HTTP 402 si está deshabilitado; requireFeatureLimit(flag, model) → 402 si count >= limit (Enterprise = Infinity).

Flags commodity siempre activos: desde la eliminación EPA-02, los 8 flags commodity ya no existen en FEATURE_FLAGS - webhooks, push_notifications, web search, memory, lead_export, widget_analytics, auto_title, synthesis_rate_limit están siempre activos en Community (las rutas commodity corren sin gating). priority_support no es un flag: es un contrato comercial/SLA (EPA-08). Quedan 11 flags enterprise, incluidos widget_credits_editing (edición de créditos widget, Community false / Enterprise true) y custom_agents como límite numérico (Community 3, Enterprise Infinity).

Tabla de flags (11)

FlagTipoCommunityEnterpriseQué gatea
sso_enabledboolfalsetrueSSO Enterprise
audit_log_immutableboolfalsetrueInmutabilidad del audit log
white_labelboolfalsetrueBranding BRANDING_*
max_workspacesnum3InfinityCreación de workspaces
max_projectsnum3InfinityCreación de proyectos
custom_agentsnum3InfinityAgentes custom (límite)
widget_enabledboolfalsetrueWidget embebible
max_widgetsnum1InfinityCreación de widgets
backup_enabledboolfalsetrueSistema de backup
max_backup_destinationsnum1InfinityDestinos de backup
widget_credits_editingboolfalsetrueEdición de créditos widget

Runtime config (DB SystemConfig)

Editable via GET/PUT /api/system/settings (bulk) y PUT /api/system/chat-retention (específica, auditada). Los defaults viven en CONFIG_DEFAULTS (packages/shared/src/constants/permissions.ts).

Grupos de claves: LLM (LLM_PROVIDER, LLM_MODEL, LLM_TEMPERATURE, ...), Embedding/Vector, Puertos/URLs del servidor, Auth (SESSION_EXPIRY), Branding (BRANDING_APP_NAME, BRANDING_PRIMARY_COLOR, BRANDING_APP_SUBTITLE, BRANDING_APP_ICON_URL - gated white_label), Feature (DISABLE_TELEMETRY, ALLOW_REGISTRATION, DLP_ENABLED), OCR (OCR_DEFAULT_MODEL, OCR_DEFAULT_MODE, OCR_DEFAULT_CUSTOM_INSTRUCTIONS, OCR_ENABLED, OCR_PRECHECK_CHARS), Synthesis (SYNTHESIS_LLM_PROVIDER_ID, SYNTHESIS_LLM_MODEL), Retention (upload_draft_retention_days, ALLOW_NON_ADMIN_UPLOAD, chat_message_retention_days).

PUT /api/system/settings siempre devuelve 200 con { updated: [...], rejected: [...] } (las claves readonly son rechazadas mientras las demás se aplican).

Retención de mensajes de chat (reaper)

Servicio: packages/server/src/services/chatMessageReaperJob.ts. Knob DB: chat_message_retention_days.

  • Pass 1 (soft-delete): tombstones mensajes de chats ACTIVOS más allá de retentionDays. No-op cuando el valor es null/""/<=0.
  • Pass 2 (hard-purge): elimina filas tombstonadas más allá de una gracia de 7 días (hardcoded). Siempre corre, independientemente de la config de retención (anti-PII leak).
  • Cadencia: setInterval de 24h, primer run a las 03:00 hora local. Mutex isRunning.
  • Solo PUT /api/system/chat-retention (auditada, confirmDataLoss: true, retentionDays: integer|null, min 1); el endpoint bulk rechaza esta clave.

Retención de backups

Por job (el campo retentionDays en BackupJob, default 30). backupRetentionService.ts limpia logs/archivos más allá de cutoffDate. No es global.

Rotación de claves de cifrado

packages/server/src/services/encryptionService.ts (AES-256-GCM, clave de 32 bytes, IV de 16 bytes, salt simmetric-chat-encryption-salt).

  • Clave actual: ENCRYPTION_KEY (base64 32 bytes) si está definida; si no scryptSync(JWT_SECRET, salt, 32).
  • LEGACY_PREVIOUS_ENCRYPTION_KEYS (CSV base64 32 bytes) → cadena de claves de descifrado [current, ...previous, scryptLegacy?].
  • encrypt() usa chain[0]. No existe job automático de re-encriptación: el procedimiento es manual decrypt-con-old → re-encrypt-con-new.