Roles de Gateway
Vincent Depassier · 30 de agosto de 2026
La cuenta de un usuario final decide quién es alguien. Un rol de gateway decide qué puede ver.
Los roles son donde vive realmente el aislamiento de datos por usuario. No le asignes ninguno y una persona logueada no alcanza nada. Asignale uno y obtiene exactamente lo que ese rol describe — incluyendo, normalmente, un filtro que restringe en silencio cada consulta a sus propias filas.
Se administran en API Gateway → Roles.

Por qué roles y no scopes de credencial
Los scopes de una credencial son fijos: quien tenga la key obtiene exactamente esas tablas y esas filas. Eso sirve para una aplicación. No puede servir para personas, porque "sus propias filas" es un conjunto distinto para cada una, y no vas a emitir una API key por cliente.
Así que el gateway lo divide:
Quien llama | Los permisos salen de |
Una aplicación con | los scopes de tabla y columna de la credencial |
Una persona con un JWT de usuario final | los scopes de los roles de su cuenta, combinados |
Mismo motor de aplicación, mismos guardrails, distinta fuente. Lo único que agregan los roles es que sus row filters pueden leer el token de quien llama — que es lo que convierte una regla en aislamiento por usuario.
Qué tiene el scope de un rol
Un rol otorga acceso de a una tabla. Cada otorgamiento lleva los mismos ajustes que un scope de credencial:

Ajuste | Default en un scope de rol nuevo |
Nivel de acceso |
|
Límite máximo | 100 filas |
Profundidad máx. de relaciones | 2 |
Permitir relaciones | activo |
Permitir agregaciones | inactivo |
Permitir introspección de esquema | inactivo |
Filtro de filas (lectura) | ninguno |
Filtro de filas (escritura) | ninguno — las escrituras reusan el de lectura |
Ojo con el primer número: un scope de rol arranca en 100 filas, más abajo que las 200 de un scope de credencial, y muy por debajo del tope de plataforma de 1.000. Una lista de cara al usuario que parece cortarse suele ser esto, no la plataforma. El valor efectivo siempre está en meta.limit.
Los scopes de columna funcionan igual que en una credencial: CanRead, CanFilter y CanSort activos por defecto, CanGroup, CanAggregate y `CanWrite` inactivos, más una regla de enmascarado y un valor por defecto opcional por columna.
El filtro de filas es el punto
Tres opciones, y la del medio es la que vas a usar.
Sin filtro. El rol ve todas las filas de la tabla. Correcto para datos de referencia — un catálogo de productos, una lista de países.
Propio — solo sus filas. La plataforma busca ella misma la primera columna de usuario final de la tabla y arma el filtro por vos. Nada que nombrar, nada que mantener, y no se rompe cuando alguien renombra la columna.
Personalizado. Elegís la columna, el operador y contra qué compara — un valor fijo, o un claim del token de quien llama:
{ "field": "Cliente", "op": "eq", "valueFromClaim": "sub" }sub es el id del usuario final autenticado. En tiempo de request el marcador se reemplaza por el valor real antes de que corra nada, y la condición resultante se une con AND a lo que haya mandado el llamador. No la puede ver, ni sacar, ni escribir esquivándola.
Si una tabla no tiene columna de usuario final, Propio no tiene a qué atarse. Agregá la columna antes de depender de él.
Lecturas y escrituras pueden tener filtros distintos
Por defecto un scope tiene un filtro y gobierna todo: una fila que no podés leer es una fila que no podés actualizar ni borrar, porque la misma condición se inyecta en el where de la mutación.
Ese default está bien la mayoría de las veces, y está mal en una forma muy común. Pensá en un canal de chat con scope Canal = X y permiso de escritura. Un solo filtro solo puede decidir qué filas se matchean, así que cualquier miembro de ese canal podría editar y borrar los mensajes de todos los demás. La regla que querías en realidad eran dos reglas:
leé toda la sala, cambiá solo lo tuyo
Por eso un scope puede llevar un filtro de escritura separado. Dejalo vacío y las escrituras reusan el de lectura — exactamente el comportamiento que tenía todo scope antes de que existiera el ajuste. Definilo, y los insert, update y delete se matchean contra él:
Filtro | Aplica a |
Filtro de filas |
|
Filtro de escritura |
|
Para el ejemplo del chat: filtro de lectura Canal eq X, filtro de escritura CREATEDBY eq {{claim:sub}}.
`CREATEDBY` es el ancla natural de un filtro de escritura. El gateway la estampa desde el token verificado de quien llama en cada insert y se niega a que un cliente la setee, así que es la única columna que nadie puede falsificar.
__SELF__ también funciona en el filtro de escritura, y un UPDATE se vuelve a verificar después de correr: si las filas que tocó no satisfacen el filtro de escritura, la transacción se revierte en vez de commitear.
Cuando alguien tiene varios roles
Los roles se combinan con semántica de unión: más roles significa más acceso, nunca menos. Las reglas exactas importan, porque la lectura intuitiva de "y además tiene el rol X" suele ser la equivocada.
Qué se combina | Regla |
Nivel de acceso | gana el más alto — |
Permitir relaciones / agregaciones / introspección | OR — si algún rol lo otorga, queda otorgado |
Límite máximo, profundidad máxima | gana el más grande |
Permisos de columna | OR por columna — si algún rol da |
Enmascarado | gana el menos enmascarado — un rol sin máscara revela una columna que otro enmascara |
Valor por defecto de columna | el primer rol que defina uno |
Filtros de lectura y de escritura | se combinan por separado, cada uno con la regla de abajo |
La trampa
Un rol sin row filter sobre una tabla da acceso sin filtrar a esa tabla — y le gana a todos los roles filtrados.
Dale a alguien Customer (solo sus filas) y Staff (sin filtro, porque el staff debería ver todo), y en cualquier tabla que ambos roles toquen, esa persona ve todo. Es el comportamiento de unión previsto, y también es cómo un rol de "dejalo ver además el dashboard" elimina en silencio una regla de aislamiento que funcionaba.
Cuando dos o más roles sí llevan filtro, sus condiciones se combinan con OR — el usuario ve la unión de lo que cada rol le habría mostrado. Los filtros de lectura y de escritura se combinan de forma independiente, así que un rol que deja vacío el de escritura amplía las escrituras aunque su filtro de lectura sea acotado.
La regla práctica: un rol que otorga acceso amplio no debería solaparse, sobre la misma tabla, con un rol que otorga acceso acotado. Si necesitás los dos, poné el otorgamiento amplio sobre otra tabla, o hacé explícito el filtro del rol amplio en vez de dejarlo vacío.
Un armado de ejemplo
La forma a la que llega casi todo producto de autoservicio:
Rol |
|
| Asignado a |
Customer | Lectura, Propio | Lectura, | todos al registrarse |
Support | Lectura, sin filtro | LecturaEscritura, sin filtro | tu equipo |
Un cliente que se loguea ve su propio registro y sus propios pedidos, sin ninguna cláusula where en el código de tu cliente. Un agente de soporte ve todos y puede actualizar un pedido.
Los dos roles se solapan en ambas tablas, así que no le des los dos a una cuenta — un cliente al que además le den Support deja de estar filtrado. Si alguien genuinamente necesita ambos sombreros, dale dos cuentas, o sacale a Support el otorgamiento sobre Clientes para que el solape desaparezca.
Asignar roles
Desde la pestaña End Users, por cuenta. Asignar es aditivo y los roles ya asignados se saltan, así que reafirmar un conjunto es seguro.
Un cambio aplica en el siguiente token del usuario. Un token de acceso ya emitido lleva los claims de rol con los que se acuñó, así que un rol recién quitado sigue funcionando hasta que ese token venza. Desactivar la cuenta revoca todas las sesiones de inmediato, que es la palanca cuando el cambio tiene que ser instantáneo.
Recomendaciones
Arrancá con un rol que filtre y agregá los más acotados después. Un workspace con un único rol
Customercon Propio en todo ya es correcto para la mayoría de los productos.Nunca dejes un row filter vacío por accidente. Vacío significa "todas las filas", no "ninguna", y es el que gana la unión.
Separá el filtro de escritura cada vez que varias personas comparten un contenedor. Un canal, un proyecto, una carpeta compartida: leé el contenedor, escribí solo tus filas.
Mantené `CanWrite` deliberado. Definir scopes de columna ya apaga la escritura; volver a darla debería ser una decisión por columna, no una pasada.
Mirá `meta.limit` antes de optimizar una lista lenta. 100 filas es el default del rol, y no es el tope de la plataforma.
Verificá un rol en el Playground. Elegí una credencial con la misma forma y corré la consulta — los resultados son lo que ese conjunto de permisos devuelve de verdad.
Siguiente
Usuarios Finales — las cuentas a las que se asignan estos roles.
Modelo de Seguridad — las seis capas por las que pasa una consulta filtrada.