Referencia de configuración
Toda la configuración de AI Workforce OS se lee de variables de entorno. Esta página lista las del backend (Go), el runtime de agentes (Python) y el frontend (Next.js), con el valor por defecto que fija el código. Es para quien instala u opera una instancia propia.
En esta página
- Cómo se carga la configuración
- Servidor y base de datos
- Autenticación, CORS y proxy
- Presupuesto, costo y límites de ejecución
- Recompensas y monedas
- Runtime: proveedores de modelo y claves
- Conexiones, sandbox, almacenamiento y push
- Límites de tasa
- Frontend: argumentos de build
- Verificación de correo y cuentas de prueba
Cómo se carga la configuración#
El backend lee el entorno al arrancar y valida la combinación antes de abrir el puerto: si algo es incoherente, el proceso termina con un error que nombra la variable. El runtime lee sus variables cuando las usa. El frontend no lee nada en ejecución: sus valores quedan fijados al compilar.
- Booleanos:
true,false,1,0y el resto de valores que aceptastrconv.ParseBool. Un valor inválido aborta el arranque. - Enteros y duraciones: un valor ausente, no numérico o menor o igual que cero se sustituye por el valor por defecto. Las duraciones usan la sintaxis de Go (
30s,5m,1h). - Importes en USD:
0es válido y desactiva el tope correspondiente; un valor negativo o no numérico deja el valor por defecto. La excepción esBUDGET_USD, que necesita un valor mayor que cero. - Listas: valores separados por comas; se ignoran los espacios.
Nota
En las tablas, «Condicional» significa que la variable solo es obligatoria en combinación con otra (por ejemplo JWT_SECRET con AUTH_ENABLED=true). «No» significa que hay un valor por defecto o que la función queda desactivada.
Servidor y base de datos#
| Variable | Por defecto | Obligatoria | Significado |
|---|---|---|---|
PORT | 8080 | No | Puerto HTTP del backend. |
DATABASE_URL | vacío | Condicional | URL de Postgres. Sin ella el backend guarda todo en memoria (solo demos). Obligatoria con AUTH_ENABLED=true. |
MIGRATE_DATABASE_URL | vacío | No | Conexión de propietario (DDL) usada solo para aplicar migraciones. Vacía: se usa DATABASE_URL. |
DB_APP_ROLE | app_user | No | Rol sin privilegios de propietario y sin BYPASSRLS al que cambia cada conexión (SET ROLE); es la base del aislamiento por RLS. Vacío desactiva el cambio. |
REDIS_URL | vacío | No | URL de Redis, con la contraseña dentro de la URL. Vacía: eventos, bloqueos y límites de tasa en memoria del proceso. |
RUNTIME_URL | http://localhost:8000 | No | URL interna del runtime de agentes. |
RUNTIME_TOKEN | vacío | No | Secreto compartido entre backend y runtime. Recomendado en producción: el backend avisa en el log si está vacío. |
READ_HEADER_TIMEOUT | 10s | No | Tiempo máximo para leer las cabeceras de una petición. |
READ_TIMEOUT | 30s | No | Tiempo máximo para leer una petición completa. |
WRITE_TIMEOUT | 60s | No | Tiempo máximo por petición REST. No se aplica al WebSocket. |
IDLE_TIMEOUT | 120s | No | Tiempo de inactividad de las conexiones keep-alive. |
MAX_BODY_BYTES | 1048576 | No | Tamaño máximo del cuerpo de una petición (1 MiB). |
APP_ENV | vacío | No | Con el valor production, el backend avisa si la KEK de la bóveda procede del entorno. |
TRACE_RETENTION_DAYS | 30 | No | Días que se conservan las trazas de razonamiento de los agentes; se purgan cada hora. |
Consejo
Define RUNTIME_TOKEN con el mismo valor en el backend y en el runtime. Con él definido, el runtime exige Authorization: Bearer <token> en todas las rutas salvo /healthz.
Autenticación, CORS y proxy#
Con AUTH_ENABLED=false el backend funciona en modo demo: una organización fija y sin inicio de sesión. Con AUTH_ENABLED=true activa cuentas, JWT, RBAC y aislamiento por organización, y el org_id sale del token.
Con la autenticación activa, el arranque falla si JWT_SECRET tiene menos de 32 bytes, si falta DATABASE_URL o si ALLOWED_ORIGINS contiene *.
| Variable | Por defecto | Obligatoria | Significado |
|---|---|---|---|
AUTH_ENABLED | false | No | Activa cuentas, JWT, RBAC y multi-tenencia. El compose de producción lo fija a true. |
JWT_SECRET | vacío | Condicional | Clave de firma de los tokens de acceso; mínimo 32 bytes; obligatoria con AUTH_ENABLED=true. Rotarla invalida todas las sesiones. |
ALLOWED_ORIGINS | vacío | Condicional | Orígenes permitidos para CORS y WebSocket, separados por comas y sin barra final. Vacía sin autenticación equivale a *; vacía con autenticación, solo el mismo origen. |
AUTH_COOKIE_SECURE | true | No | Atributo Secure de la cookie del refresh token. Solo se desactiva en desarrollo local con HTTP plano. |
AUTH_COOKIE_NAME | wfai_refresh | No | Nombre de la cookie del refresh token. Cámbialo si varias instalaciones comparten host. |
TRUSTED_PROXIES | vacío | No | CIDR o IP de los proxies cuyo X-Forwarded-For se respeta (lo usan los límites de tasa). Un valor inválido aborta el arranque. |
ENABLE_DEMO_RESET | false | No | Registra POST /api/v1/demo/reset, que borra los datos de ejecución. Déjala en false en producción. |
KILL_SWITCH | vacío | No | freeze o lockdown: anulación global que ninguna llamada de API puede levantar. Se relee con SIGHUP. |
UI_BASE_URL | vacío | No | URL pública del frontend: destino del navegador tras el callback OAuth y base de los enlaces de verificación de correo. |
Atención
Con AUTH_ENABLED=false no hay inicio de sesión: cualquiera que alcance la URL controla la organización demo. No expongas ese modo a internet con datos o claves reales.
Presupuesto, costo y límites de ejecución#
Estas variables controlan el gasto y la concurrencia del orquestador. Las ventanas de contexto y los controles de calidad tienen más ajustes internos con valores por defecto razonables; esta tabla recoge los que conviene conocer.
| Variable | Por defecto | Obligatoria | Significado |
|---|---|---|---|
BUDGET_USD | 50 | No | Tope de gasto en USD. El compose de producción usa 25 si no la defines. |
REQUEST_BUDGET_CAP_USD | 0 | No | Tope duro de gasto por solicitud. 0 lo desactiva. |
AGENT_BUDGET_USD | 0 | No | Tope mensual de gasto por agente. 0 lo desactiva. |
COST_CONFIRM_THRESHOLD_USD | 1.0 | No | Costo estimado a partir del cual se pide confirmación antes de ejecutar. |
BUDGET_PAUSE_TIMEOUT | 30m | No | Tiempo que una solicitud pausada por un tope espera a que alguien decida. |
PRICE_IN_PER_M / PRICE_OUT_PER_M | tabla de precios interna | No | Fuerzan la tarifa en USD por millón de tokens de entrada y salida. Hay que definir las dos; las leen el backend y el runtime. |
APPROVAL_ACTIONS | send_proposal,send_contract | No | Acciones que siempre exigen aprobación humana. |
APPROVAL_TIMEOUT | 30m | No | Tiempo de espera de una aprobación dentro de una solicitud. |
STANDING_APPROVAL_DAYS | 30 | No | Vigencia de un permiso permanente por proyecto (máximo 90). También termina con el proyecto. |
MAX_PARALLEL / MAX_PARALLEL_PER_ORG | 4 / 8 | No | Tareas simultáneas en total y llamadas al runtime en vuelo por organización; el resto espera en cola. |
MAX_PLAN_DEPTH | 30 | No | Profundidad máxima de un plan. |
TASK_TIMEOUT | 120s | No | Tiempo máximo de una tarea. |
TASK_RETRIES / TASK_MAX_ATTEMPTS | 3 / 2 | No | Reintentos y número máximo de intentos por tarea. |
TASK_RETRY_BACKOFF | 2s | No | Espera entre reintentos de una tarea. |
PROJECT_MAX_NODES / PROJECT_MAX_CHILDREN_PER_GROUP / PROJECT_MAX_ACTIVE | 20000 / 200 / 20 | No | Tamaño máximo de un proyecto y número de proyectos activos por organización. |
PLANNER_MAX_PHASES / PLANNER_MAX_TASKS_PER_PHASE / PLANNER_CONCURRENCY | 12 / 40 / 3 | No | Cotas del planificador de proyectos. |
DEP_CONTEXT_TOKEN_BUDGET | 8000 | No | Tokens de contexto que una tarea recibe de las tareas de las que depende. |
EMAIL_HOLD_SECONDS | 60 | No | Ventana para cancelar un correo antes de que salga. Mínimo 60 y máximo 300: los valores fuera de rango se ajustan. |
Recompensas y monedas#
El sistema de monedas premia el trabajo completado. REWARDS_RULES acepta un JSON que se aplica encima de las reglas por defecto, así que basta con incluir los campos que quieras cambiar.
Si el JSON tiene un valor inválido, el backend lo registra en el log y usa las reglas por defecto completas: nunca aplica una economía a medias.
{
"enabled": true,
"base": { "S": 5, "M": 10, "L": 20, "XL": 40 },
"default_complexity": "M",
"first_try_bonus_pct": 50,
"rework_pct": 50,
"project_base": 20,
"project_per_task": 2,
"project_max": 100,
"min_project_tasks": 2
}
| Variable | Por defecto | Obligatoria | Significado |
|---|---|---|---|
REWARDS_ENABLED | true | No | false, 0 u off desactivan por completo la obtención de monedas. |
REWARDS_RULES | vacío | No | JSON con reglas: enabled, base (S, M, L, XL), default_complexity, first_try_bonus_pct, rework_pct, project_base, project_per_task, project_max, min_project_tasks. |
REWARDS_WELCOME_COINS | 1000 | No | Monedas de bienvenida que recibe cada organización una sola vez, la primera vez que abre la tienda o la oficina. 0 lo desactiva. |
Runtime: proveedores de modelo y claves#
El runtime admite cinco proveedores: deepseek, anthropic, openai, kimi y custom (un endpoint compatible con OpenAI, por ejemplo vLLM u Ollama). Un proveedor cuenta como configurado cuando su clave (y, para custom, su URL base) está en el entorno. Sin ningún proveedor configurado, o con SIMULATION=true, el runtime usa el motor simulado: respuestas guionadas, sin claves y sin costo.
Ante un error transitorio (timeout, límite de tasa, 5xx) reintenta en el mismo proveedor y después pasa al siguiente permitido. ALLOWED_PROVIDERS es el techo del operador: ningún dato llega a un proveedor excluido, ni siquiera como respaldo. Cada organización puede restringirlo más desde sus ajustes, nunca ampliarlo, y puede guardar su propia clave de proveedor, que gana a la del entorno.
| Variable | Por defecto | Obligatoria | Significado |
|---|---|---|---|
SIMULATION | false | No | true fuerza el motor simulado aunque haya claves. |
DEEPSEEK_API_KEY / DEEPSEEK_MODEL | vacío / deepseek/deepseek-chat | Condicional | Clave y modelo de DeepSeek, el proveedor por defecto de la plataforma. |
ANTHROPIC_API_KEY / ANTHROPIC_MODEL | vacío / anthropic/claude-sonnet-5-5 | Condicional | Clave y modelo de Anthropic. |
OPENAI_API_KEY / OPENAI_MODEL / OPENAI_BASE_URL | vacío / openai/gpt-4.1 / vacío | Condicional | Clave, modelo y URL base opcional de OpenAI. |
KIMI_API_KEY / KIMI_MODEL / KIMI_BASE_URL | vacío / openai/kimi-k2-0905-preview / https://api.moonshot.ai/v1 | Condicional | Clave, modelo y URL base de Kimi (Moonshot). |
CUSTOM_LLM_BASE_URL | vacío | Condicional | URL base de un endpoint compatible con OpenAI. Activa el proveedor custom. |
CUSTOM_LLM_API_KEY / CUSTOM_LLM_MODEL | vacío | No | Clave opcional (muchos servidores locales no la piden) y nombre del modelo del endpoint custom. |
CUSTOM_PRICE_IN_PER_M / CUSTOM_PRICE_OUT_PER_M | 3 / 15 | No | USD por millón de tokens del endpoint custom. Puede ser 0 para un modelo local. |
MODEL | vacío | No | Variable heredada: proveedor/modelo aplica a ese proveedor; un nombre sin prefijo aplica al primario. |
MODEL_PROVIDER_ORDER | deepseek,anthropic,openai,kimi,custom | No | Orden global de respaldo. MODEL_PROVIDER_ORDER_<ROL> lo cambia para un rol de agente o un tipo de llamada (por ejemplo MODEL_PROVIDER_ORDER_PLAN). |
ALLOWED_PROVIDERS | vacío | No | Techo del operador: solo estos proveedores pueden recibir datos. Vacío significa todos los configurados. Defínela igual en el backend y en el runtime. |
PROVIDER_RETRIES | 1 | No | Reintentos en el mismo proveedor antes de pasar al siguiente. |
PROVIDER_RETRY_BACKOFF_S | 1.0 | No | Espera entre reintentos, en segundos. |
PROVIDER_TIMEOUT_S | 120 | No | Timeout por llamada al proveedor, en segundos. |
SYNTH_PROMPT_TOKEN_BUDGET | 24000 | No | Presupuesto de tokens del prompt de síntesis en el runtime. |
Atención
Las claves de proveedor son secretos que cuestan dinero si se filtran. Pásalas por el entorno del servicio, nunca en una imagen ni en el repositorio.
Conexiones, sandbox, almacenamiento y push#
Sin CONNECTIONS_KEK no se puede guardar ninguna credencial real: la bóveda falla cerrada y solo funcionan las conexiones simuladas. La KEK es base64 de 32 bytes aleatorios; guárdala aparte de la base de datos, porque sin ella los secretos cifrados son irrecuperables.
El sandbox de código es opcional y necesita el servicio sandbox-runner, que recibe acceso al socket de Docker. Lee antes la página del modelo de seguridad.
openssl rand -base64 32
| Variable | Por defecto | Obligatoria | Significado |
|---|---|---|---|
CONNECTIONS_KEK | vacío | No | Clave de cifrado de claves de la bóveda (base64 de 32 bytes). La necesitan las conexiones reales y las claves de modelo propias de cada organización. |
CONNECTIONS_KEK_PREVIOUS | vacío | No | KEK anterior, solo mientras dura una rotación. |
CUSTOM_API_ALLOW_PRIVATE | false | No | Si es true, las fuentes de API genéricas (custom_api) pueden llegar a direcciones privadas, como un ERP en tu red local. Solo para instalaciones propias en una red de confianza; nunca cambia la regla de solo GET. |
GOOGLE_OAUTH_CLIENT_ID / GOOGLE_OAUTH_CLIENT_SECRET | vacío | No | Tu propia aplicación OAuth de Google para las conexiones. |
LINKEDIN_OAUTH_CLIENT_ID / LINKEDIN_OAUTH_CLIENT_SECRET | vacío | No | Tu propia aplicación OAuth de LinkedIn. |
OAUTH_REDIRECT_URL | http://localhost:8080/api/v1/connections/oauth/callback | No | URL pública del callback OAuth; debe coincidir con la registrada en el proveedor. |
GITHUB_API_URL | vacío (api.github.com) | No | Base de la API de GitHub, por ejemplo para GitHub Enterprise. |
SANDBOX_URL / SANDBOX_TOKEN | vacío | No | Dirección y token del sandbox-runner. Sin las dos, los agentes no pueden ejecutar código. |
SANDBOX_IMAGE / SANDBOX_RUNTIME / SANDBOX_SECCOMP_PROFILE | vacío | No | Las lee el sandbox-runner: imagen de ejecución, runtime de contenedor (por ejemplo runsc de gVisor) y perfil seccomp. |
S3_ENDPOINT / S3_BUCKET / S3_REGION / S3_PREFIX / S3_USE_PATH_STYLE | vacío (S3_USE_PATH_STYLE: true) | No | Almacenamiento compatible con S3 para los archivos del Drive. Sin endpoint y bucket se usa Postgres, o memoria si no hay base de datos. |
S3_ACCESS_KEY / S3_SECRET_KEY | vacío | No | Credenciales S3. |
DRIVE_MAX_FILE_MB / DRIVE_QUOTA_MB | 25 / 5120 | No | Tamaño máximo por archivo y cuota por organización, en MB. |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT | vacío | No | Las tres juntas activan las notificaciones Web Push; sin ellas quedan desactivadas. |
PUSH_ALLOWED_HOSTS | vacío | No | Hosts de servicios push que se añaden a la lista permitida por defecto. |
Límites de tasa#
Los límites de tasa están integrados en el backend y no tienen variables de ajuste: por ejemplo, 10 intentos de inicio de sesión cada 15 minutos y 120 comandos por minuto. Los de autenticación se guardan en Redis si hay REDIS_URL, y en memoria del proceso si no.
Lo que sí se configura es TRUSTED_PROXIES. Solo la cabecera X-Forwarded-For de esos proxies sirve para identificar al cliente. Detrás de Caddy, indica la red del proxy: sin ella, todos los clientes comparten la IP del proxy y el mismo contador.
Frontend: argumentos de build#
El frontend se compila con salida standalone. Las variables NEXT_PUBLIC_* se incorporan al compilar como argumentos del Dockerfile (ARG); cambiarlas exige reconstruir la imagen.
Dejarlas vacías da el comportamiento recomendado para un solo dominio: la API en la ruta relativa /api/v1 y el WebSocket derivado de window.location.
docker build \
--build-arg NEXT_PUBLIC_API_URL=https://app.example.com/api/v1 \
--build-arg NEXT_PUBLIC_WS_URL=wss://app.example.com/ws \
-t aiworkforce-frontend ./frontend
| Argumento de build | Por defecto | Obligatoria | Significado |
|---|---|---|---|
NEXT_PUBLIC_API_URL | vacío | No | URL base absoluta de la API, por ejemplo https://app.example.com/api/v1. Vacía: ruta relativa. |
NEXT_PUBLIC_WS_URL | vacío | No | URL absoluta del WebSocket, por ejemplo wss://app.example.com/ws. Vacía: se deriva de window.location. |
NEXT_PUBLIC_MOCK | vacío | No | Con el valor true, el frontend funciona con datos simulados, sin backend. |
Verificación de correo y cuentas de prueba#
Dos funciones opcionales de acceso, las dos requieren AUTH_ENABLED=true. La verificación de correo exige un SMTP que funcione: con EMAIL_VERIFICATION=true y sin SMTP válido el backend se niega a arrancar, de modo que un servidor a medio configurar no deja a nadie fuera.
Las cuentas de prueba permiten usar la app sin registrarse; cada una tiene su propia organización y deja de funcionar sola.
| Variable | Por defecto | Significado |
|---|---|---|
EMAIL_VERIFICATION | false | Las cuentas nuevas deben abrir un enlace de un solo uso enviado a su correo antes de entrar. Requiere AUTH_ENABLED, UI_BASE_URL y SMTP_* (o EMAIL_DEV_LOG). |
EMAIL_DEV_LOG | false | Solo desarrollo: los enlaces de verificación se escriben en el log en lugar de enviarse. |
SMTP_HOST | vacío | Servidor SMTP. Obligatorio con EMAIL_VERIFICATION, salvo que EMAIL_DEV_LOG esté activo. |
SMTP_PORT | 465 | 465 usa TLS implícito; 587 usa STARTTLS. |
SMTP_TLS | tls con el puerto 465; starttls con otro | tls, starttls o none (none solo para un relay local). |
SMTP_USER / SMTP_PASS | vacío | Credenciales del servidor SMTP; define las dos o ninguna. |
SMTP_FROM | vacío | Remitente, por ejemplo AI Workforce OS <no-reply@example.com>. Obligatorio con verificación por SMTP. |
TRIAL_ENABLED | false | Activa POST /api/v1/auth/trial: cuentas desechables con su propia organización. |
TRIAL_TTL_HOURS | 24 | Horas que funciona una cuenta de prueba. |
TRIAL_PER_IP_PER_DAY | 3 | Cuentas de prueba que una misma dirección IP puede crear al día. |
TRIAL_GLOBAL_PER_DAY | 300 | Cuentas de prueba creadas al día entre todas las direcciones. |
TRIAL_EMAIL_DOMAIN | prueba.invalid | Dominio de las direcciones generadas para las pruebas (sin buzón detrás). |