Documentación

Referencia de la API

ollaya serve expone dos API en http://localhost:11435:

  • la API nativa bajo /api/*, inspirada en la de Ollama, para decisiones y gestión de modelos;
  • la API compatible con TypeSafe bajo /v1/*, idéntica a nivel de cable a la de TypeSafe, para que los SDK de TypeSafe existentes funcionen sin cambios. Consulta Compatibilidad con TypeSafe.
Método Ruta Propósito
GET, HEAD / Comprobación de vida (liveness): Ollaya is running
GET /api/version Versión del servidor
POST /api/decide Responde preguntas tipadas sobre un estado; también carga y descarga de la memoria un modelo
GET /api/tags Modelos de esta máquina
POST /api/show Detalles de un modelo
GET /api/ps Modelos cargados en memoria
POST /api/pull Descarga un modelo (transmite el progreso)
DELETE /api/delete Elimina un modelo
POST /api/copy Copia un modelo a un nombre nuevo
POST /api/create Crea un modelo a partir de otro (transmite el progreso)
POST /v1/systemone TypeSafe System One
POST /v1/decisions Alias de /v1/systemone
GET /v1/models Lista de modelos de TypeSafe

/api/push y /api/blobs/:digest están reservados y responden 501 NOT_IMPLEMENTED. Los endpoints de texto de Ollama (/api/generate, /api/chat, /api/embed) responden 404: los modelos de decisión nunca generan texto.

Convenciones

  • JSON. Los cuerpos de solicitud y respuesta son objetos JSON. El cuerpo se analiza como JSON sea cual sea su Content-Type, así que curl -d funciona tal cual. Las solicitudes son de 8 MiB como máximo.
  • Nombres de campos en snake_case. Los campos de solicitud desconocidos se ignoran; null significa ausente.
  • Nombres de modelos con la forma [host/][namespace/]model[:tag], sin distinguir mayúsculas de minúsculas. Una etiqueta ausente significa latest. Las respuestas siempre usan la forma canónica, como laya:latest.
  • Números. Las probabilidades, las confianzas, score y noul se redondean a 4 decimales. Las duraciones son enteros en nanosegundos; las marcas de tiempo son RFC 3339 en UTC.
  • Streaming. /api/pull y /api/create transmiten JSON delimitado por saltos de línea, un objeto por línea, y terminan con exactamente un {"status":"success"} o una línea de error. Envía "stream": false para una sola respuesta.
  • IDs de solicitud. Cada respuesta lleva X-Request-Id, y las de /v1/* también x-typesafe-request-id. Un X-Request-Id válido enviado por el cliente se devuelve tal cual.
  • Concurrencia. Un modelo cargado ejecuta una solicitud a la vez, y cada solicitud responde todas sus preguntas en una sola pasada. Las solicitudes a un mismo modelo se ponen en cola, así que enviar más a la vez no termina antes; el viaje de ida y vuelta de cada una incluye entonces la espera. Haz todas las preguntas sobre un estado en una sola solicitud. Los distintos modelos cargados se ejecutan en paralelo.
  • Sin descargas implícitas. Ningún endpoint descarga un modelo como efecto secundario. ollaya run descarga primero; las aplicaciones llaman a /api/pull.

Errores

Cada error, en cada endpoint, tiene este cuerpo:

{
  "error": "model \"laya:xl\" not found, try pulling it first",
  "code": "MODEL_NOT_FOUND"
}
Campo Significado
error Mensaje legible por humanos. No lo analices; el único mensaje congelado es model "<name>" not found, try pulling it first, como en Ollama.
code Código legible por máquinas. Ramifica según este.
detail Solo para INVALID_REQUEST, TOO_MANY_OPTIONS, INPUT_TOO_LONG y STATE_TRUNCATED: cada problema de validación, con la forma ValidationError de TypeSafe (FastAPI): loc, msg, type y a veces ctx.
Código HTTP Cuándo Reintento
INVALID_JSON 400 Falta el cuerpo, no es JSON o no es un objeto no
INVALID_REQUEST 422 El cuerpo no pasa la validación; detail enumera cada problema no
TOO_MANY_OPTIONS 422 Las opciones de una pregunta no caben en el presupuesto de opciones del modelo no
INPUT_TOO_LONG 422 state supera los 65,536 tokens no
STATE_TRUNCATED 422 /v1/systemone o /v1/decisions descartaría parte de state para caber en el contexto del modelo no
UNAUTHORIZED 401 OLLAYA_API_KEY está definida y la solicitud no lleva la clave no
FORBIDDEN 403 La cabecera Origin o Host del navegador no está permitida no
MODEL_NOT_FOUND 404 El modelo (o el objetivo de un router) no está en esta máquina; en una descarga, no está en el registro no
NOT_FOUND 404 No existe tal endpoint no
METHOD_NOT_ALLOWED 405 El endpoint existe, el método no no
OPERATION_IN_PROGRESS 409 Una descarga o una creación está escribiendo el mismo nombre de modelo después de que termine
REQUEST_TOO_LARGE 413 El cuerpo supera los 8 MiB no
QUEUE_FULL 503 Ya hay OLLAYA_MAX_QUEUE solicitudes esperando; se envía con Retry-After: 1 sí
MODEL_LOAD_FAILED 500 El modelo no se pudo cargar (archivos corruptos, memoria, OLLAYA_LOAD_TIMEOUT) rara vez
INFERENCE_FAILED 500 El runner falló durante una decisión sí
STORAGE_ERROR 500 Disco lleno, permisos o E/S no
INTERNAL 500 Un bug; el registro del servidor tiene los detalles bajo el ID de solicitud sí
UNSUPPORTED_MODEL 501 Esta compilación no puede ejecutar el formato del modelo no
NOT_IMPLEMENTED 501 Endpoint reservado no
REGISTRY_ERROR 502 El registro no está accesible o no es válido sí
DIGEST_MISMATCH 502 Una descarga no coincidió con su sha256 y se descartó sí

El conjunto de códigos es abierto: trata un código desconocido según su estado HTTP. Un error de validación enumera todos los problemas a la vez:

{
  "error": "state: Field required; questions.urgency.score.criteria: List should have at least 2 items after validation, not 1",
  "code": "INVALID_REQUEST",
  "detail": [
    {"loc": ["body", "state"], "msg": "Field required", "type": "missing"},
    {
      "loc": ["body", "questions", "urgency", "score", "criteria"],
      "msg": "List should have at least 2 items after validation, not 1",
      "type": "too_short",
      "ctx": {"field_type": "List", "min_length": 2, "actual_length": 1}
    }
  ]
}

Una vez que ha empezado un stream, un fallo llega como una última línea con la misma forma, como {"error": "…", "code": "DIGEST_MISMATCH"}. Comprueba en cada línea el campo error antes de leerla como progreso.

Preguntas

/api/decide, /v1/systemone y /api/create comparten un único esquema de preguntas, el de TypeSafe. Una solicitud tiene 1–256 preguntas, indexadas por cualquier id; las respuestas vuelven en el mismo orden.

type instructions criteria Respuesta
choice opcional obligatorio: etiqueta de objeto → descripción, o un array de etiquetas; 2–255 opciones choice, confidence, probabilities
score opcional obligatorio: array de descripciones de nivel, el nivel 0 primero; 2–10 niveles score, confidence, legend, probabilities
noul opcional opcional: {"true": "…", "false": "…"} noul
  • instructions puede ser una cadena, un objeto, un array o null. Cuando falta o es null, el modelo lee en su lugar el id de la pregunta, así que un id descriptivo como is_spam funciona por sí solo.
  • state es una cadena, un objeto o un array, de hasta 65,536 tokens. Si supera el contexto disponible del modelo, /api/decide lo trunca e informa state_truncated: true. /v1/systemone y /v1/decisions devuelven 422 STATE_TRUNCATED con el modelo que respondió en detail[0].ctx.model.
  • Límites del modelo. Cada opción necesita sitio en el contexto del modelo: unas 125 opciones para laya:en (512 tokens) y 250 para laya:multilingual (1,024). Más es 422 TOO_MANY_OPTIONS. Para un router, se aplican los límites del objetivo.

Las respuestas son las formas de TypeSafe, en este orden de campos:

type Campos
choice choice: la etiqueta más probable. confidence. probabilities: etiqueta → probabilidad, en el orden de los criterios.
score score: el nivel esperado Σ i·pᵢ, que puede caer entre niveles. confidence. legend: "0"… → la descripción del nivel. probabilities: "0"… → probabilidad.
noul noul: la probabilidad de que la afirmación se cumpla. Sin confidence, como en TypeSafe.

confidence es la probabilidad superior normalizada de TypeSafe, (K · pmax − 1) / (K − 1) para K opciones: 0 cuando todas las opciones son igual de probables, 1 cuando una opción tiene toda la probabilidad. La fórmula es la misma para todos los modelos, pero lo que significa una confianza dada no lo es: los modelos están calibrados de forma distinta, así que ajusta un umbral por modelo con tus propios datos. Las probabilidades se calibran con las temperaturas de cada modelo. En una GPU CUDA se ejecuta el grafo fp16, cuyas respuestas pueden diferir de las de fp32 en empates ajustados.

keep_alive

Cuánto tiempo permanece cargado un modelo después de que termina una solicitud, con la semántica de Ollama:

Valor Significado
"5m", "1h30m", "300ms", 300, "300" Permanece cargado ese tiempo tras la solicitud
0, "0", "0s" Se descarga de la memoria en cuanto termina la solicitud
-1, "-5m", cualquier valor negativo Permanece cargado hasta que el servidor se detiene o hasta una descarga de memoria explícita
ausente o null OLLAYA_KEEP_ALIVE, por defecto 5m

El temporizador arranca cuando termina una solicitud, y gana el valor de la solicitud más reciente. Para un router, se aplica al objetivo que respondió. /v1/* ignora keep_alive.

Decide

POST /api/decide

Responde preguntas tipadas sobre un estado en una sola pasada hacia adelante. El cuerpo es el de /v1/systemone más opciones nativas; la respuesta es la de TypeSafe más campos nativos, así que un cliente de TypeSafe también puede analizarla.

Campo Tipo Obligatorio Notas
model string sí Nombre del modelo
state string, object or array sí para decidir Sin él, la solicitud carga o descarga de la memoria el modelo (abajo)
questions object sí, salvo que el modelo tenga preguntas integradas Sustituye por completo las preguntas propias del modelo
preset string no Nombre de un preajuste, integrado o personalizado, en lugar de questions
images array of strings no Para un modelo de visión: imágenes PNG, en base64 o URLs data: en base64. Decider admite una; winnow:e4b-vision admite hasta 16. Consulta Imágenes
keep_alive string or number no Consulta keep_alive
extras array of strings no ["laya"] añade a cada respuesta la confianza propia de laya y la probabilidad de acto
stream boolean no Reservado; true se rechaza
curl http://localhost:11435/api/decide -d '{
  "model": "laya",
  "state": "I was charged twice for my subscription this month. Please refund the second charge.",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this ticket?",
      "criteria": {
        "billing": "Payments, invoices and refunds",
        "technical": "Bugs, errors and outages",
        "account": "Login, profile and settings"
      }
    },
    "urgency": {
      "type": "score",
      "instructions": "How urgent is this ticket?",
      "criteria": ["Can wait", "Needs attention this week", "Needs attention today"]
    },
    "refund": {
      "type": "noul",
      "instructions": "The customer asks for money back.",
      "criteria": {"true": "Asks for a refund", "false": "Does not ask for a refund"}
    }
  },
  "keep_alive": "10m"
}'
{
  "model": "laya:en",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "billing",
      "confidence": 0.7781,
      "probabilities": {"billing": 0.8521, "technical": 0.0611, "account": 0.0868}
    },
    "urgency": {
      "type": "score",
      "score": 1.1982,
      "confidence": 0.3418,
      "legend": {"0": "Can wait", "1": "Needs attention this week", "2": "Needs attention today"},
      "probabilities": {"0": 0.1203, "1": 0.5612, "2": 0.3185}
    },
    "refund": {"type": "noul", "noul": 0.9127}
  },
  "usage": {"input_tokens": 118, "output_tokens": 0},
  "routing": {
    "router": "laya:latest",
    "model": "laya:en",
    "route": "english",
    "reason": "English Latin text"
  },
  "state_truncated": false,
  "done_reason": "decide",
  "created_at": "2026-09-24T09:30:12.418Z",
  "total_duration": 18734512,
  "load_duration": 0,
  "eval_duration": 16302117
}
Campo Significado
model El modelo que respondió: para un router, su objetivo (laya:en para una solicitud laya)
answers id de pregunta → respuesta, en el orden de las preguntas
usage input_tokens leídos; output_tokens siempre es 0
routing Para un router: router, el model elegido, una clave route estable y un reason informativo. null en caso contrario.
state_truncated true si se descartó parte del estado para caber en el contexto del modelo
done_reason "decide", "load" o "unload"
created_at Cuándo se produjo la respuesta
total_duration Nanosegundos desde que se recibe la solicitud hasta la respuesta, incluida la cola
load_duration Nanosegundos esperando a que se cargue el modelo; 0 cuando ya estaba caliente
eval_duration Nanosegundos en el runner: tokenización, pasada hacia adelante, calibración

Con "extras": ["laya"], cada respuesta tiene además un objeto laya: confidence (la confianza de laya basada en la entropía) y act_probability (de la cabeza de acto del modelo, o null).

Imágenes

Un modelo de visión (decider:2b-vision o winnow:e4b-vision) responde preguntas sobre una imagen además de sobre el estado. Envía la imagen en images, codificada en base64, igual que funciona el images de Ollama:

curl http://localhost:11435/api/decide -d '{
  "model": "decider:2b-vision",
  "state": "A photo from the warehouse camera.",
  "images": ["'"$(base64 -w0 shelf.png)"'"],
  "questions": {
    "blocked": {"type": "noul", "instructions": "Is the aisle blocked?"},
    "fill": {"type": "score", "instructions": "How full is the shelf?", "criteria": ["empty", "half full", "full"]}
  }
}'
  • Decider: Una imagen por solicitud, solo PNG. El preprocesamiento del modelo se reproduce valor por valor, así que los píxeles tienen que coincidir con lo que decodifican los autores del modelo. Los decodificadores JPEG de Rust difieren de libjpeg-turbo en hasta 4 niveles en algunos píxeles, así que JPEG aún no se acepta: conviértelo antes a PNG.
  • Decider: La imagen se redimensiona a múltiplos de 32 píxeles, como espera el modelo, y después puede tener como máximo 4,096 parches de 16x16 píxeles, alrededor de un megapíxel (1024x1024). Una imagen más grande recibe un 422 que lo dice; redúcela primero.
  • Decider: Las preguntas admiten como máximo 10 opciones. El mismo modelo también responde solicitudes solo de texto.
  • Winnow E4B vision: hasta 16 PNG ordenados, 2–64 opciones por pregunta, dentro del contexto combinado de imagen/estado/pregunta. El proyector correspondiente se descarga por separado de la misma revisión del autor. Las etiquetas de texto existentes de Winnow no lo cargan.
  • Un modelo que no lee imágenes responde a una solicitud con images con un 422.

/v1/systemone y /v1/decisions siguen siendo idénticos a la API de TypeSafe, que no tiene campo de imagen.

Carga y descarga de la memoria. Una solicitud sin state ni questions nunca decide. Sin keep_alive, o con uno positivo o negativo, carga el modelo (cada objetivo, para un router) y devuelve done_reason: "load". Con keep_alive: 0 lo descarga de la memoria ("unload"). ollaya run precarga de esta forma, y ollaya stop descarga de la memoria.

curl http://localhost:11435/api/decide -d '{"model": "laya:en", "keep_alive": -1}'
curl http://localhost:11435/api/decide -d '{"model": "laya:en", "keep_alive": 0}'

Una decisión no tiene efecto secundario sobre los datos almacenados, así que es seguro reintentarla.

Preajustes

Un preajuste es un conjunto de preguntas con nombre. Hay seis integrados (triage, email, guard, moderation, router, agent), y puedes guardar los tuyos. Envía "preset": "NAME" a /api/decide en lugar de questions.

curl http://localhost:11435/api/presets/create -d '{
  "name": "billing-check",
  "description": "Billing, and how upset the customer is",
  "questions": {
    "billing": {"type": "noul", "instructions": "The message is about a charge, an invoice or a refund."},
    "tone": {"type": "choice", "instructions": "How does the customer sound?", "criteria": {"calm": null, "annoyed": null, "angry": null}}
  }
}'
curl http://localhost:11435/api/decide -d '{"model": "winnow:e4b", "state": "I was charged twice this month.", "preset": "billing-check"}'
Endpoint Cuerpo Efecto
GET /api/presets – Preajustes integrados primero, luego los personalizados: name, builtin, description, ids de pregunta, modified_at
POST /api/presets/create name, questions, description (opcional) Guarda un preajuste personalizado, reemplazando el que tenga el mismo nombre
POST /api/presets/show name Un preajuste con sus preguntas
DELETE /api/presets/delete name Elimina un preajuste personalizado

Los nombres tienen de 1 a 64 caracteres de letras minúsculas, dígitos, - y _. Un nombre integrado no se puede reutilizar (422) ni eliminar (403), y un nombre desconocido es un 404. Los preajustes personalizados se guardan junto a los modelos, así que cada cliente del servidor ve los mismos.

Routers

Un router como laya (laya:latest) no tiene pesos: en cada solicitud elige uno de sus objetivos, que es el que responde. laya solo lee state:

Estado route Responde
Inglés english laya:en
Mayormente escritura no latina (árabe, cirílica, CJK, …) multilingual laya:multilingual
Escritura latina, pero no inglés (turco, alemán, …) multilingual laya:multilingual
Sin letras english (el valor por defecto) laya:en

Texto corto en mayúsculas y sin letras acentuadas, como nombres de comercios en un extracto de tarjeta (MIGROS KADIKOY ISTANBUL TR), SKUs o nombres de usuario, por lo general no se puede identificar y va a laya:en. Si conoces el idioma, pide laya:multilingual o laya:en directamente; el model de la respuesta dice qué checkpoint respondió.

El enrutamiento cuesta microsegundos. Ramifica según route, nunca según reason, cuya redacción puede cambiar. laya:typed-decisions nunca lo elige el router; pídelo directamente.

Listar los modelos locales

GET /api/tags

Los modelos de esta máquina, los más recientes primero. Cada entrada tiene name, model (el mismo), modified_at, size en bytes, digest (sha256 del manifiesto, en hexadecimal sin prefijo) y details: parent_model, format (onnx, gguf o router), family, families, parameter_size y quantization_level (las precisiones que lleva, como F16/F32, o la cuantización de un modelo GGUF, como Q8_0).

{
  "models": [
    {
      "name": "laya:en",
      "model": "laya:en",
      "modified_at": "2026-09-24T08:11:02.117Z",
      "size": 853634822,
      "digest": "bf30e4654e9483ff1e6a4fe6fb21b8a71baff6c8a01013046e7d13339020efd7",
      "details": {
        "parent_model": "",
        "format": "onnx",
        "family": "laya",
        "families": ["laya"],
        "parameter_size": "421M",
        "quantization_level": "F16/F32"
      }
    }
  ]
}

Mostrar los detalles de un modelo

POST /api/show
curl http://localhost:11435/api/show -d '{"model": "laya:en"}'
Campo Significado
license Texto de la licencia
modelfile Un Modelfile que recrea el modelo
parameters Parámetros definidos en el modelo, un name value por línea, como precision fp32
questions Preguntas integradas, o null
router Para un router: strategy, default y routes (route → model). null en caso contrario.
details Como en /api/tags
model_info general.architecture, general.languages, general.source (el repositorio fijo de Hugging Face), más claves propias de la familia como laya.context_length. general.languages enumera los idiomas para los que se entrenó y evaluó el modelo (multilingual en muchos casos); un modelo construido sobre una base multilingüe puede leer aún otros idiomas, así que mide con tus datos.
capabilities Tipos de pregunta que responde (choice, score, noul), más act si tiene cabeza de acto
modified_at Como en /api/tags

Un router se muestra como él mismo, sin resolverse a un objetivo.

Listar los modelos en ejecución

GET /api/ps

Los modelos cargados, ordenados por nombre. Los routers nunca aparecen; sus objetivos cargados sí. Cada entrada tiene name, model, size (memoria, RAM más VRAM), digest, details (con la precisión cargada realmente: F16 o F32, o la cuantización de un modelo GGUF), expires_at (cuándo se descargará de la memoria, o null cuando se mantiene cargado), size_vram, context_length y device (cpu, cuda:0, metal, …).

Descargar un modelo

POST /api/pull
{"model": "laya:en"}

Descarga el modelo al almacén local y verifica cada blob contra su sha256. Descargar un router descarga también cada modelo al que enruta. Solo se descargan las capas que necesita esta máquina, los blobs compartidos entre modelos se descargan una vez, y las descargas interrumpidas se reanudan.

La respuesta transmite el progreso, con las cadenas de estado de Ollama:

{"status":"pulling manifest"}
{"status":"pulling 891102d37268","digest":"sha256:891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c","total":842609210,"completed":420557117}
{"status":"pulling 891102d37268","digest":"sha256:891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c","total":842609210,"completed":842609210}
{"status":"verifying sha256 digest"}
{"status":"writing manifest"}
{"status":"success"}

Un modelo solo aparece en /api/tags después de writing manifest. Para un router hay un único success, al final del todo. Un nombre que no se puede analizar, un modelo que no está en el registro y un registro inaccesible son errores HTTP normales (422, 404, 502) antes de que empiece el stream, así que curl --fail funciona. Con "stream": false la respuesta es {"status": "success"} cuando termina. Una segunda descarga del mismo nombre se une a la que está en curso. Es seguro reintentar.

Eliminar un modelo

DELETE /api/delete
{"model": "triage"}

Elimina el nombre, y los blobs que no usa ningún otro modelo. Un modelo cargado se descarga de la memoria cuando terminan sus solicitudes; eliminar un router conserva sus objetivos. La respuesta es 200 con cuerpo vacío, y 404 MODEL_NOT_FOUND cuando el nombre no existe; tras un timeout, trátalo como éxito.

Copiar un modelo

POST /api/copy
{"source": "laya:en", "destination": "my-guardrail"}

Copia un modelo a un nombre nuevo, sobrescribiendo un destino existente. La respuesta es 200 con cuerpo vacío.

Crear un modelo

POST /api/create

La API que hay detrás de ollaya create -f Modelfile: la CLI lee el Modelfile y los archivos que menciona y envía sus contenidos como JSON.

Campo Tipo Obligatorio Notas
model string sí Nombre que crear
from string sí Un modelo local, posiblemente un router. Nunca se descarga.
questions object no Preguntas integradas, validadas como una solicitud de decisión
calibration object no temperature: hasta 3 números (choice, score, noul). temperature_by_options: "<type>:<2|3-5|6-10|11+>" → number.
parameters object no precision: "fp16" o "fp32", para fijar un grafo
license string or array no Texto(s) de licencia
description string no Una línea, mostrada por /v1/models y ollaya show
stream boolean no Por defecto true
curl http://localhost:11435/api/create -d '{
  "model": "triage",
  "from": "laya:en",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this ticket?",
      "criteria": ["billing", "technical", "account"]
    }
  },
  "parameters": {"precision": "fp32"},
  "description": "Support ticket triage"
}'

El stream informa using existing layer sha256:… por cada capa heredada, creating new layer sha256:… por cada capa nueva, y luego writing manifest y success. Las capas están direccionadas por contenido, así que repetir una creación da el mismo modelo.

Versión

GET /api/version
{"version": "0.1.0"}

Endpoints compatibles con TypeSafe

Endpoint Descripción
POST /v1/systemone Solicitud: model, state (obligatorio) y questions. Respuesta: exactamente model, answers y usage.
POST /v1/decisions Alias de /v1/systemone
GET /v1/models Los modelos locales, como {"models": [{"name", "description", "release_date"}]}

/v1/* ignora campos nativos como keep_alive y extras, y nunca añade campos nativos a sus respuestas. Los errores usan el mismo cuerpo que /api/*, que el SDK de TypeSafe lee correctamente. Consulta Compatibilidad con TypeSafe.

Seguridad

El servidor se enlaza a 127.0.0.1:11435 y, como Ollama, confía en los llamadores locales. Enlazarlo a otra dirección (OLLAYA_HOST=0.0.0.0) permite que todo el que pueda alcanzar el puerto ejecute decisiones y descargue, elimine y cree modelos, así que:

  • OLLAYA_API_KEY hace que cada solicitud salvo GET /, HEAD / y el preflight CORS requiera Authorization: Bearer <key>; de lo contrario la respuesta es 401 UNAUTHORIZED. El SDK de TypeSafe envía su clave así, y la CLI ollaya envía $OLLAYA_API_KEY. El servidor registra un aviso cuando escucha más allá de loopback sin una clave.
  • TLS no lo termina el servidor; pon un proxy inverso delante para el acceso remoto.
  • Navegadores. Las solicitudes con una cabecera Origin solo se permiten desde localhost, 127.0.0.1, 0.0.0.0 y [::1] (cualquier puerto), los webviews de aplicaciones y editores, y los orígenes en OLLAYA_ORIGINS (separados por comas, con comodines *). Un servidor en loopback también rechaza cabeceras Host inesperadas, lo que bloquea el DNS rebinding.
  • Tus datos. Los estados y las preguntas nunca se registran ni se repiten en los errores.
Variable Por defecto Efecto
OLLAYA_HOST 127.0.0.1:11435 Dirección de enlace; el objetivo del cliente. Una dirección de loopback también escucha en [::1], así que los programas de Windows alcanzan sin demora un servidor en WSL en localhost
OLLAYA_API_KEY sin definir Exige Authorization: Bearer <key>
OLLAYA_ORIGINS sin definir Orígenes de navegador permitidos adicionales
OLLAYA_KEEP_ALIVE 5m keep_alive por defecto
OLLAYA_MAX_LOADED_MODELS 3 Límite de modelos cargados
OLLAYA_MAX_QUEUE 512 Solicitudes en curso antes de 503 QUEUE_FULL
OLLAYA_LOAD_TIMEOUT 5m Plazo de carga antes de 500 MODEL_LOAD_FAILED
OLLAYA_DEVICE auto auto, cpu, cuda o cuda:<n>
OLLAYA_MODELS ~/.ollaya/models Almacén de modelos
OLLAYA_REGISTRY ollaya.dev Host de registro por defecto en los nombres