Saltar al contenido principal

Quién puede ver qué

Un panel que cualquiera con la URL puede leer es una captura de pantalla con pasos extra. La razón para mantener los paneles en una plataforma mantenida en lugar de en una carpeta de HTML exportado es que la plataforma recuerda quién tiene permiso para abrirlos — y sigue recordándolo después de que la persona que los construyó se haya ido.

Esta página es el mapa honesto de ese modelo: qué aplica hoy, dónde se detiene, y qué partes puedes alcanzar desde código.

Los tres roles​

Cada membresía de una cuenta lleva exactamente un rol:

RolValorQué significa
AdminadminSe salta cada comprobación de derechos de acceso. Gestiona miembros, grupos y concesiones.
CreadorcreatorPuede crear y modificar. Sujeto a los derechos de acceso.
VisorviewerSolo lectura. Rechazado en cualquier ruta que modifique.

Tres cosas sobre esto vale la pena saber antes de diseñar en torno a ello.

El rol vive en la fila de la membresía, no en el token. El JWT no lleva ninguna reclamación de rol, así que cada decisión de rol es una lectura de base de datos sobre el par cuenta/usuario. Eso es un poco más lento y mucho más difícil de falsificar.

Falla cerrado. Una membresía cuyo rol almacenado no se reconoce — un valor heredado, una errata — se resuelve como viewer, no como admin.

Admin es una anulación, no un rango superior. Un admin no es "un creador con más concesiones": la comprobación de permisos se cortocircuita a permitir antes de que se consulte ninguna concesión. No puedes denegar a un admin fuera de una carpeta.

Una clave de API actúa como la persona que la creó

Una clave de API se resuelve a su usuario creador, con el rol de ese usuario. Una clave generada por un admin es una clave de admin, y todo lo de abajo se aplica exactamente igual que a esa persona en el navegador. No hay un alcance de permisos separado y más estrecho para las claves, y una clave no expira cuando el rol de su creador se rebaja — revoca la clave.

Veinticuatro rutas están deliberadamente cerradas a las claves y necesitan una sesión de navegador real: facturación y pagos (16 de ellas), la propia gestión de claves de API, leer tu suscripción, eliminación de cuenta, cambio de contraseña, telemetría, y firma de tokens de incrustación. Todo lo demás acepta una clave — incluyendo cada ruta de derechos de acceso, grupo, carpeta, panel y conjunto de datos.

Una excepción deliberada. Pasar un panel a public está protegido por un interruptor de cuenta, apiKeysMayPublish, que es false por defecto y solo puede activarlo un admin desde un navegador. Así que una clave — y por tanto un agente que la tenga — puede construir, publicar y compartir dentro de tu cuenta, pero por defecto no puede ser lo que hace algo legible para todo el mundo. Mira Publicar y compartir.

Grupos​

Un grupo es una bolsa con nombre de usuarios dentro de una cuenta. Concedes al grupo; la membresía cambia sin tocar la concesión.

Dos límites, ambos deliberados:

  • Los grupos contienen usuarios, no otros grupos. No hay anidamiento, así que no hay jerarquía de grupos que razonar.
  • Las lecturas están abiertas a cada miembro de la cuenta; las escrituras son solo para admins. Cualquier miembro puede listar los grupos y ver quién está en ellos. Solo un admin puede crear, renombrar, eliminar, o cambiar la membresía.

Los grupos sí están en el SDK:

from vibedasher.models import GroupCreate, GroupUpdate

created = client.groups.create(GroupCreate(name="analytics", description="Read-only analysts"))
client.groups.update(created.group.id, GroupUpdate(user_ids_to_add=["<user-id>"]))

for g in client.groups.list().groups:
print(g.name, g.user_count)
const created = await client.groups.create({ name: 'analytics', description: 'Read-only analysts' });
await client.groups.update(created.group.id, { userIdsToAdd: ['<user-id>'] });

const { groups } = await client.groups.list();

Derechos de acceso — la concesión está en la carpeta​

Esta es la frase individual más importante de esta página, y la que más probablemente te sorprenda:

Una concesión nombra una carpeta, nunca un panel o conjunto de datos individual.

Un derecho de acceso es una fila con esta forma:

(account, folder, resource type, permission, principal) → allow | deny
  • resource type — dataset o viz. Son los dos que una concesión puede nombrar; mira Qué puede nombrar una concesión para lo que gobierna el resto.
  • permission — view, edit, delete, create.
  • principal — un usuario o un grupo.
  • allow / deny — los dos tipos de acceso.

Un panel está cubierto por una concesión porque está dentro de la carpeta concedida. Muévelo a otra carpeta y pasa a estar gobernado por las concesiones de esa carpeta en su lugar. No hay ninguna concesión por panel que perder de vista, que es la mitad buena de este diseño; la otra mitad es que la carpeta es la unidad más pequeña que puedes conceder, así que una carpeta que compartes es una carpeta que compartes por completo.

Eso no es un techo para lo estrecho que puede ser un share​

La frase anterior habla del permiso, no del share. Una carpeta es lo más pequeño que puedes nombrar en un permiso — y tú decides qué hay dentro de una carpeta. Para compartir exactamente un panel, dale una carpeta propia, que son tres llamadas que tu agente ya tiene:

create_folder(name="Q3 board pack")
move_to_folder(resource_type="viz", resource_id=<vizId>, folder_id=<newFolderId>)
grant_access(folder_id=<newFolderId>, resource_type="viz",
principal_id=<userId>, principal_type="user", permissions=["view"])

resource_type es dataset o viz en el movimiento, principal_type es user o group, y access_type vale allow por defecto — deny es el otro valor, y vence a cualquier allow.

Estas tres son herramientas MCP, y el MCP es la vía soportada para esto. Las rutas de access-rights se mantienen deliberadamente fuera del SDK publicado y de la referencia de API — son fontanería de control de acceso cuya forma no hemos comprometido, así que no existe método de cliente generado para ellas y no debería confiarse en ninguno. create_folder es la de las tres con equivalente en el SDK, client.folders.create.

Mover un panel cambia qué permisos lo gobiernan y nada más — el panel conserva su id, y su enlace no se ve afectado. Así que «compartir por panel es imposible» es la conclusión equivocada a extraer de la regla de la carpeta: son tres llamadas, y la carpeta es cómo lo expresas.

Las dos mitades de un permiso de panel​

Un panel y los datos que hay debajo se gobiernan por tipos de recurso separados, así que un share que solo nombra uno de ellos está a medio hacer. viz:view permite a alguien abrir el panel; dataset:view es lo que permite a sus tarjetas devolver cifras. La siguiente sección es por qué eso no es un detalle que puedas posponer.

Cómo se toma realmente una decisión​

En orden. Gana la primera regla que coincida:

  1. Admin → permitir. Incondicionalmente, antes de leer ninguna concesión.
  2. El principal interno system → permitir. Esta es la identidad propia de los workers de la plataforma (trabajos de actualización, ejecuciones programadas); no es una cuenta en la que puedas iniciar sesión.
  3. Propiedad → permitir. El usuario propietario de la fila siempre puede leerla y cambiarla.
  4. En cualquier otro caso, deciden las concesiones de la carpeta, y una sola denegación vence a todos los permisos.

Vale la pena precisar lo de "la denegación gana", porque es más estrecho de lo que suena. Una denegación quita una carpeta del conjunto que se te concedió — no resta de las reglas 1–3. No puedes denegar a un admin, y no puedes denegar a alguien fuera de una fila que posee. Si tu modelo mental es "denegar es el botón de parada de emergencia", no lo es: es una resta dentro de la capa de concesiones solamente.

Dos comportamientos que te van a morder​

Las concesiones no descienden en cascada a las subcarpetas. La comprobación es una prueba plana de pertenencia a un conjunto sobre el folder_id propio del recurso. Las carpetas sí tienen una relación padre/hijo, y la interfaz la dibuja como un árbol — pero ningún código de autorización la recorre. Una concesión sobre Marketing no llega a Marketing / Q3. Concede cada carpeta que quieras conceder.

Una denegación sobre create antes no hacía nada — corregido el 2026-09-08. La comprobación de permiso de creación solo leía las filas allow; el derecho de arranque de un Creador para crear en la raíz de la cuenta o en una carpeta que posee, y una fila allow explícita en otro sitio, se saltaban por completo cualquier deny. La fila explícita create / deny de un admin ahora anula ambas — la misma regla de "una sola denegación vence a todos los permisos" que el resto de esta página describe para ver/editar/eliminar. Los admins siguen siendo indeniegables, igual que con cualquier otro permiso de aquí.

Quién puede cambiar una concesión​

Un admin de cuenta, o el propietario de la carpeta sobre la que está la concesión. Leer las concesiones está abierto a cualquier miembro de la cuenta: el estado de compartición se renderiza en la aplicación en páginas que cualquier miembro puede abrir, así que la lectura está deliberadamente sin restringir. Trata la lista de concesiones en sí como información visible para toda la cuenta.

Qué puede nombrar una concesión​

Dos tipos de recurso admiten concesión: viz (paneles) y dataset. Construye tu modelo de gobernanza con esos dos, guardados en carpetas: es la forma que el modelo aplica y la única que ofrece el diálogo. POST /v1/access-rights rechaza cualquier otro tipo de recurso con un 400, en el momento en que lo escribes y no en el momento en que alguien confía en ello.

Tres cosas están gobernadas por algo distinto de una concesión, y saber cuál por qué es todo el sentido de esta sección:

RecursoQué lo gobierna
CarpetaLa pertenencia a la cuenta para lecturas; el rol para mutaciones. Una carpeta es el contenedor que una concesión nombra, no algo contra lo que se escribe una concesión.
Planificación de actualizaciónLa propiedad, más la frontera de la cuenta.
PreguntaSolo el propietario. Una pregunta no puede compartirse por ningún mecanismo.
TareaLa ACL del panel al que pertenece.
ConexiónSu propietario y los administradores de la cuenta. Cualquier Creador puede crear una.

Una lista vacía te dice de qué tipo de vacío se trata. GET /v1/access-rights/{folder}/{type} lleva un indicador enforced, así que total: 0 para un tipo no concedible significa ningún derecho de este tipo puede existir en ningún sitio, mientras que total: 0 para viz significa nadie tiene uno. Las filas escritas antes del rechazo siguen listadas, y siguen siendo eliminables, con enforced: false al lado.

Compartir un panel sin compartir sus datos no funciona​

Esta es la pregunta que el modelo no responde como la mayoría espera, así que merece una frase directa: viz:view por sí solo no le da a nadie un panel que funcione.

Las tarjetas de un panel obtienen sus cifras con POST /v1/query, y esa ruta autoriza cada conjunto de datos participante a través de la ACL de conjuntos de datos, en modo denegar-en-lugar-de-descartar: un solo conjunto de datos que el lector no puede leer rechaza la consulta entera con 403 query_dataset_forbidden. Nunca se ejecuta sobre el subconjunto que sí tiene permitido. Así que «que el becario vea este panel pero no los datos detrás» produce un panel cuyas tarjetas fallan, no un panel con las cifras ocultas.

Concede también dataset:view sobre la carpeta que contiene los conjuntos de datos — viz:view y dataset:view van en pareja, y un permiso que solo lleva el primero produce un panel en el que cada tarjeta da 403. Si los conjuntos de datos no deben compartirse en bloque, dales una carpeta propia igual que hace la receta de arriba. O, si el objetivo real es mostrar un resultado sin entregar los datos subyacentes, publica el panel o incrústalo. Ambas vías se ejecutan bajo la identidad de servicio de la plataforma y nunca consultan esta ACL, que es exactamente por lo que pueden mostrar una cifra a alguien que no puede consultarla.

Comprobar tu propio trabajo​

Cada campo anterior describe una regla; ninguno te dice con qué acaba una persona concreta, y eso no se lee en la lista de concesiones. Los derechos se componen — pertenencias a grupos, un deny que vence a todos los allow incluido uno heredado de un grupo, la separación por tipo de recurso y el control de rol independiente sobre la mutación — en un resultado que ninguna lista muestra. El deny heredado es el caso que importa: nunca aparece en la lista de allow que lees, así que «por qué esta persona no ve esto» no tiene respuesta a partir de la lista sola.

GET /v1/access-rights/{folder}/effective?userId= lo resuelve. Para cada par (tipo de recurso, permiso) devuelve qué decide la ACL, qué regla decidió, si el rol de la persona lo permite, si el tipo está aplicado, el producto de todo ello — y la fila de derecho de acceso que produjo el veredicto, que es el id a revocar cuando la respuesta es un deny. Tu agente tiene lo mismo como describe_effective_access.

Responde solo a la pregunta de la ACL, y lo dice en una lista caveats presente en cada respuesta: no puede ver las filas que la persona posee (la propiedad es por fila y vence a la ACL de carpeta), no sabe nada de las vías de panel público ni de incrustación y — según la sección anterior — un panel que funciona necesita también derechos sobre sus conjuntos de datos. Siempre puedes preguntar por ti mismo; preguntar por otra persona requiere la misma posición que cambiar la ACL: un admin de la cuenta o el propietario de la carpeta.

Alcanzar esto desde código​

Superficie¿En los SDKs?
Grupos (crear, listar, actualizar, membresía)Sí — client.groups.*
Usuarios (listar, actualizar, rol, membresía de grupo)Sí — client.users.*
CuentaSí — client.accounts.*
Derechos de acceso (las concesiones en sí)No

Los derechos de acceso están excluidos de la superficie del SDK generado, así que no hay client.accessRights ni una entrada para ellos en la API Reference. Las rutas existen y aceptan una clave de API — sencillamente no forman parte del contrato del cliente publicado y versionado, y preferimos decírtelo a dejarte construir contra una forma con la que no nos hemos comprometido. Gestiona las concesiones en la aplicación, o desde tu agente: el servidor MCP expone herramientas de conceder, revocar y listar derechos de acceso que llaman a las mismas rutas bajo tu propia identidad, y heredan la misma regla de admin-o-propietario-de-carpeta.

Filtrado a nivel de fila: el carril de incrustación, y solo el carril de incrustación​

La seguridad a nivel de fila es real en Vibedasher, pero vive en un solo lugar: paneles incrustados detrás de un token firmado.

Cuando tu servidor genera un token de incrustación, puede adjuntar filtros a la carga del token. En cada consulta la plataforma reescribe cada referencia a un conjunto de datos como una subconsulta filtrada, así que el visor no puede leer físicamente filas fuera de su porción. Los filtros vienen exclusivamente del token verificado — nunca del cuerpo de la petición — así que un visor no puede ampliar su propio alcance editando una petición. Un filtro que nombra una columna que no existe en ningún esquema de conjunto de datos se rechaza con un 403 en lugar de ignorarse en silencio, porque un filtro que calladamente no hace nada es un agujero.

Tres límites a tener en cuenta:

  • Solo se aplica al carril de incrustación. El carril de consulta del SDK no tiene inyección obligatoria de filtros: una clave de API es un llamante de servidor de confianza, y limitar a sus propios usuarios es tu trabajo, no el de la plataforma.
  • La ACL de carpeta no es a nivel de fila. Decide qué paneles y conjuntos de datos existen para ti, no qué filas dentro de ellos puedes leer. Son preguntas distintas y Vibedasher las responde con maquinaria distinta.
  • Un panel que consulta en el navegador no se puede acotar en absoluto — por diseño. Este es el punto que hay que decidir antes de construir, no después. Un panel autoalojado en el carril sin credenciales — el árbol expulsado cuyo dataLane es wasm, o cualquier cosa que hayas construido tú mismo sobre un extracto Parquet o JSON — descarga los datos y ejecuta cada consulta en el navegador del visitante. El extracto se direcciona por conjunto de datos e instantánea, no por consulta, así que contiene todas las filas de ese conjunto de datos, y cada visitante recibe el mismo archivo. No queda ningún servidor en el camino que pueda estrecharlo: un filtro escrito en la página cambia lo que se dibuja, nunca lo que se entregó.
El alcance por fila y el carril sin credenciales se excluyen mutuamente

Todo el atractivo de ese carril es que no hay clave en el paquete, ni token que generar, ni backend propio que mantener en marcha — y el precio es exactamente esa misma propiedad vista desde el otro lado: nada puede darle a un visor menos filas que a otro. No es un hueco que se vaya a cerrar; es lo que significa poner los datos en el navegador.

Si algunos visores deben ver menos filas, mantén la consulta en un servidor. Dos montajes lo hacen, y los dos mantienen la credencial fuera de la página: tu propio backend genera un token de incrustación de corta duración que lleva el filtro (la plataforma añade ese predicado a cada consulta que autoriza, antes de cualquier join o agregación, de modo que la página no puede ampliarlo), o código de servidor de confianza consulta con una clave de API y devuelve filas que tú ya has acotado. Elegir primero el carril rápido y "acotarlo" después con un filtro en la página produce un panel que parece correcto, no lanza ningún error, y entrega todas las filas.

Mira Incrustación para cómo se genera y se presenta un token.

Autenticación en dos pasos​

Correo + contraseña es la única entrada a Vibedasher que descansa en un solo secreto. Los inicios de sesión con Google y LinkedIn llevan el segundo factor de su proveedor; las claves API y el token CLI de vibedasher login no son inicios de sesión. Por eso el segundo factor es opcional, por usuario, solo en el inicio de sesión con contraseña: un código TOTP de seis dígitos de cualquier aplicación de autenticación (Google Authenticator, 1Password, Authy…), activado desde Configuración → Autenticación en dos pasos escaneando un código QR y confirmando un código. Al activarlo recibes ocho códigos de recuperación de un solo uso, mostrados una única vez: un teléfono perdido inicia sesión con uno de ellos.

Lo que cambia al iniciar sesión: tras una contraseña correcta, el navegador no recibe sesión; recibe un desafío de cinco minutos y un solo propósito que únicamente el paso del código puede canjear. La API nunca ve ese desafío como una credencial: lleva una audiencia que la API rechaza de plano, así que no puede reproducirse contra api.*.vibedasher.com ni colarse en la cookie de sesión. Cinco códigos erróneos bloquean el desafío durante quince minutos; un código vale una sola vez.

Tres límites que conviene conocer:

  • La activación es individual. Cada miembro la activa para su propia cuenta; no es algo que un administrador encienda para todo el mundo.
  • Los inicios de sesión OAuth nunca reciben el desafío, por diseño: añadir nuestro factor encima del del proveedor es fricción sin seguridad.
  • El desbloqueo es una acción de administrador. Un miembro sin teléfono y sin código de recuperación acude a un administrador de la cuenta, que restablece su segundo factor desde la página del miembro (la acción queda registrada). Un propietario que es el único administrador, sin código de recuperación, pasa por soporte.

Una configuración práctica​

Para un equipo de diez con un par de conjuntos de datos sensibles:

  1. Haz admins a la una o dos personas que administran la cuenta. Recuerda que son ingobernables por concesiones — ese es el sentido del rol, y la razón para mantener la lista corta.
  2. Todo el que construye es un creador; todo el que solo lee es un visor. El rol de visor es un rechazo firme sobre mutación, así que es un límite real, no una convención de interfaz.
  3. Pon paneles y conjuntos de datos en carpetas que coincidan con cómo quieres compartirlos — una carpeta por audiencia, no por tema. La carpeta es la concesión.
  4. Concede a grupos, no a personas. La concesión entonces sobrevive a que alguien se una o se vaya.
  5. No crees carpetas anidadas esperando que la concesión de la carpeta padre las alcance. Concede cada una.
  6. Si un panel tiene que llegar a personas fuera de la cuenta, publícalo e incrústalo con un token firmado que lleve sus filtros de fila — no les des una cuenta.