Skip to content

Seguridad y límites ​

Como el canal de agentes es público, ur trae dos capas de defensa entre el mensaje del usuario y el modelo: límites de abuso (cuánto se puede hablar) y filtros de contenido (qué se puede decir).

Las dos se configuran en el panel. Lo importante para tu integración es cómo se manifiestan en la respuesta, porque no se parecen a un error de API.

El canal público es anónimo salvo que pidas usuario ​

/embed/* no lleva ningún secreto, y es una decisión deliberada: un <script> en el navegador no puede guardar uno, así que su modelo de confianza es distinto al de la API, que usa un access token. Por defecto, la identidad de conversación es un subjectId que genera el propio navegador, y lo que contiene el abuso son los límites de esta página.

La lista de orígenes permitidos del canal es una medida higiénica (evita que alguien copie tu snippet en otro sitio), no un control de acceso: un cliente que no sea un navegador puede omitir la cabecera Origin.

Lo que exija saber quién pregunta va con un usuario identificado: tu backend firma un token corto para el usuario de tu aplicación, el navegador lo manda en cada petición y ur lo verifica antes de abrir la conversación. La conversación pasa a ser de ese usuario, y las tools reciben quién es. El token lo firma siempre tu backend: ninguna clave va al navegador.

El orden en que se aplica todo ​

Cada mensaje pasa por estas puertas, en este orden:

  1. Límite de peticiones — por IP: el límite del agente y un tope general sumando todos los agentes. Devuelve un error HTTP de verdad (429, con Retry-After).
    • Usuario identificado, si el agente lo pide: el token se verifica aquí, después del canal y el origen. Si no pasa, 401, sin abrir conversación ni gastar nada de lo que sigue. Ver Usuarios identificados.
  2. ¿El agente está activo? — el interruptor general del agente.
  3. ¿Le quedan turnos a la conversación? — si el agente tiene límite de turnos y ya se agotó, el mensaje se descarta.
  4. Longitud — se recorta a maxMessageLength.
  5. Límite de ráfaga — mensajes demasiado seguidos.
  6. Límite por hora — cuántos mensajes por cliente y hora.
  7. Filtros de contenido — las cinco comprobaciones de abajo.

Si alguna bloquea, el turno no llega al modelo y se responde con un mensaje del catálogo.

Un bloqueo no es un error HTTP ​

Esto es lo que más sorprende al integrar

Un mensaje bloqueado devuelve 200, no 4xx. Para el usuario final es indistinguible de una respuesta normal: recibe un texto y la conversación sigue.

En el widget y el SDK llegan los tokens del mensaje de bloqueo y luego el fin de turno, igual que cualquier respuesta. onError no dispara.

Si tu UI depende de detectar bloqueos, no puede: por el canal público un bloqueo no se distingue de una respuesta, a propósito.

La excepción es lo anterior a todo esto: el límite de peticiones, que sí devuelve 429, y el token de un usuario identificado que no pasa, que devuelve 401. Y el fin de conversación por límite de turnos, que sí se distingue en todas las vías, porque tu UI tiene que cerrar la caja de texto.

Por defecto, el texto que se devuelve no dice qué comprobación saltó: todas las categorías del catálogo salen con el mismo texto genérico, y conviene dejarlo así para no darle a quien sondea un mapa de los filtros. El motivo real de los bloqueos por entropía, palabras prohibidas o firewall semántico queda en el registro de intercepciones de la ficha del agente; los bloqueos por límite de ráfaga o por hora no se registran ahí.

Límites de abuso ​

LímitePor defectoComportamiento
Longitud máxima2000 caracteresRecorta en silencio, no rechaza
Mensajes por horaSin límiteBloquea con mensaje de catálogo
RáfagaDesactivadoBloquea mensajes demasiado seguidos
Turnos por conversaciónSin límiteTras el último, la conversación se cierra
Peticiones por minuto desde una IP180Siempre activo. Al pasarlo, 429 hasta el minuto siguiente (widget y SDK)

El límite por IP es por seguridad

Los demás límites reconocen al visitante por una huella que guarda su navegador, y un script puede inventarse una en cada petición; la IP no. Por eso este no se puede apagar. Cuenta todo lo que el widget o el SDK piden al agente desde una IP: cargarse, recuperar la conversación y cada mensaje. Detrás de una oficina o una VPN todos salen por la misma IP: si el agente se usa así, súbelo en su sección Seguridad. Por encima hay un tope general por IP para todos los agentes juntos (Ajustes → API, 600 por defecto), que es un freno de emergencia.

La longitud recorta, no falla

Un mensaje más largo que el límite no se rechaza: se corta y se procesa el principio. Si tu aplicación permite pegar textos largos, valida tú la longitud antes de enviar, o el usuario verá una respuesta a media pregunta sin saber por qué.

Quién cuenta como "un cliente" a efectos de estos límites: en el widget y el SDK es una huella que el navegador guarda aparte y que no se reinicia al empezar conversación nueva — justo para que resetear no sirva para esquivar el límite. Con un usuario identificado, el cliente es ese usuario: su cuota es la misma desde cualquier navegador.

Límite de turnos por conversación ​

Acota cuánto puede hablar alguien en una conversación, pensado sobre todo para un chat en una web pública. Se configura en la ficha del agente, sección Seguridad, con dos campos:

  • Turnos máximos por conversación. El último turno recibe su respuesta normal y, detrás, el mensaje Límite de conversación del catálogo. A partir de ahí la conversación está cerrada: cualquier mensaje que llegue se descarta sin llamar al modelo y sin guardarse, y se vuelve a contestar con ese mismo texto.
  • Olvidar la conversación tras (horas). Pasado ese tiempo sin que el navegador envíe nada, el widget o el SDK estrenan una conversación nueva, con los turnos a cero. Sin este ajuste, quien agota los turnos no puede volver a escribir desde ese navegador.

Qué cuenta como turno: un mensaje que llega al modelo. Los bloqueados por las puertas de arriba (seguridad, ráfaga, límite por hora, agente desactivado) no gastan turno, y un turno en el que el modelo falla se devuelve.

«Cerrada» no se guarda en ningún sitio: se calcula comparando los turnos hechos con el máximo vigente. Si subes o quitas el límite, las conversaciones cerradas se reabren; si lo bajas, se cierran las que ya lo superan. Los turnos se cuentan desde que se instaló la versión que trae el límite; las conversaciones anteriores empiezan en cero.

Cómo le llega el cierre a cada vía:

  • Widget — pinta el mensaje de cierre como una burbuja propia, deja la caja de texto visible pero deshabilitada y, mientras el agente tenga límite, no ofrece Reiniciar conversación.
  • SDK — onConversationEnd(texto), y en el store conversationEnded: true con el mensaje marcado isConversationEnd. Ver SDK JavaScript.

No es una barrera

La conversación vive en el navegador (un identificador en localStorage). Quien lo borre, o abra una ventana privada, empieza una conversación nueva con todos sus turnos. Sirve para la mayoría de visitantes, no contra alguien decidido; para eso están el límite por hora y el de ráfaga, que se cuentan con una huella que no se reinicia con la conversación, y contra un script que se invente la huella, el límite por IP.

Filtros de contenido ​

Cinco comprobaciones, en orden fijo. Las dos primeras son atajos: si pasan, se salta el resto.

#ComprobaciónQué hace
1Palabras segurasSi el mensaje completo coincide con una frase de la lista, pasa directo
2Solo emojisUn mensaje sin texto pasa directo
3EntropíaDetecta cargas ofuscadas (cadenas hexadecimales, ruido, leet excesivo)
4Palabras prohibidasCoincidencia literal, con detección leet y difusa opcionales
5Firewall semánticoCompara el significado con frases de ejemplo, por similitud

En las palabras seguras, «coincide» quiere decir el mensaje entero. No cuentan las mayúsculas, las tildes, los signos de puntuación ni los espacios de más, pero solo unos pocos: «¡Hola!» pasa con la frase hola, y con más de 16 de esos caracteres (signos, espacios, caracteres invisibles…) el mensaje ya no pasa por este atajo. Todo lo demás cuenta: las letras y números de cualquier alfabeto («hola» seguido de una frase en chino no es hola) y los emoji y demás símbolos («hola 👋» tampoco; el mensaje que es solo emoji tiene su propio atajo, el siguiente). Para dejar pasar «gracias 🙏», añade la frase tal cual: casa con ese emoji, con cualquier tono de piel, y solo con él (gracias a secas necesita su propia frase). Una frase en otro alfabeto (ありがとう, привет) casa con ese mismo texto, y un mensaje que esconde texto en caracteres invisibles no pasa por este atajo.

Las reglas son globales; los interruptores, por agente

Las listas de palabras, las palabras seguras y las frases del firewall se definen una vez para toda la instalación, en Ajustes → Seguridad agentes del panel, una sección cada una (Palabras prohibidas, Firewall semántico, Palabras seguras y Detector de entropía). Sus categorías y el modelo de embeddings del firewall están en Ajustes → Sistema → Seguridad. Lo que se elige por agente es cuáles de las cinco comprobaciones se aplican y qué texto se devuelve al bloquear.

Las cuatro comprobaciones con configuración propia (palabras seguras, entropía, palabras prohibidas y firewall semántico) tienen en el panel un botón Probar texto para ver qué habría saltado. En la ficha del agente, Probar mensaje evalúa las cinco de una vez con los interruptores que tengas puestos en pantalla. Ninguno invoca al modelo del agente — el del firewall semántico sí calcula el embedding del texto que le pases. Y los dos recortan el texto como el chat: Probar mensaje, a la longitud máxima guardada del agente, y Probar texto, a 20.000 caracteres, lo más que puede recibir un agente.

Los textos que ve el usuario ​

Todos los mensajes de bloqueo salen de un catálogo editable — en el panel, Ajustes → Mensajes agentes: agente desactivado, límite por hora, ráfaga, límite de conversación, entropía, proveedor saturado, error del modelo, y uno por cada categoría de palabras prohibidas y del firewall.

Se resuelven en este orden: texto propio del agente → texto global → constante de emergencia. Así que puedes dejar el global para toda la instalación y cambiar solo el de un agente concreto.

Las entradas se crean solas al arrancar; en el panel solo se editan los textos.

Requisito de infraestructura ​

Sin Redis no hay chat

El límite de ráfaga y el rate limit por IP de todas las rutas públicas cuentan en Redis (el tope por hora se persiste en Postgres). Si Redis no está disponible, las rutas con rate limit devuelven 503 en vez de dejar pasar el tráfico — falla cerrado, a propósito. Tenlo presente al diagnosticar una caída: un 503 en /embed/* apunta a Redis, no al modelo.