Pular para o conteúdo principal

Quem pode ver o quê

Um painel que qualquer pessoa com a URL pode ler é uma captura de tela com passos extras. A razão para manter painéis em uma plataforma mantida, em vez de em uma pasta de HTML exportado, é que a plataforma lembra quem tem permissão para abri-los — e continua lembrando depois que a pessoa que os construiu segue em frente.

Esta página é o mapa honesto desse modelo: o que ele aplica hoje, onde ele para, e quais partes você pode alcançar a partir de código.

Os três papéis​

Toda associação a uma conta carrega exatamente um papel:

PapelValorO que significa
AdminadminIgnora toda verificação de direitos de acesso. Gerencia membros, grupos e concessões.
CriadorcreatorPode criar e alterar. Sujeito a direitos de acesso.
VisualizadorviewerSomente leitura. Recusado em qualquer rota que altera dados.

Três coisas sobre isso valem a pena saber antes de você projetar algo em torno disso.

O papel vive na linha de associação, não no token. O JWT não carrega nenhuma claim de papel, então toda decisão de papel é uma leitura no banco de dados sobre o par conta/usuário. Isso é um pouco mais lento e muito mais difícil de falsificar.

Ele falha fechado. Uma associação cujo papel armazenado não é reconhecido — um valor legado, um erro de digitação — resolve para viewer, não para admin.

Admin é uma substituição, não uma patente máxima. Um admin não é "um criador com mais concessões": a verificação de permissão atalha diretamente para permitir antes que qualquer concessão seja consultada. Você não pode negar um admin o acesso a uma pasta.

Uma chave de API age como a pessoa que a criou

Uma chave de API resolve para o usuário que a criou, com o papel desse usuário. Uma chave gerada por um admin é uma chave de admin, e tudo abaixo se aplica a ela exatamente como se aplica a essa pessoa no navegador. Não há um escopo de permissão separado e mais restrito para chaves, e uma chave não expira quando o papel de quem a criou é rebaixado — revogue a chave.

Vinte e quatro rotas são deliberadamente fechadas para chaves e precisam de uma sessão de navegador real: cobrança e pagamento (16 delas), o próprio gerenciamento de chaves de API, a leitura da sua assinatura, exclusão de conta, troca de senha, telemetria e assinatura de token de incorporação. Tudo o mais aceita uma chave — incluindo toda rota de direitos de acesso, grupo, pasta, painel e conjunto de dados.

Uma exceção deliberada. Tornar um painel public é controlado por uma chave da conta, apiKeysMayPublish, que tem false como padrão e só pode ser ativada por um admin em um navegador. Então uma chave — e, portanto, um agente que a possui — pode construir, publicar e compartilhar dentro da sua conta, mas por padrão não pode ser o que torna algo legível pelo mundo todo. Veja Publique e compartilhe.

Grupos​

Um grupo é um conjunto nomeado de usuários dentro de uma conta. Você concede ao grupo; a associação muda sem tocar na concessão.

Dois limites, ambos deliberados:

  • Grupos contêm usuários, não outros grupos. Não há aninhamento, então não há hierarquia de grupos para se preocupar.
  • Leituras são abertas a todo membro da conta; escritas são exclusivas de admin. Qualquer membro pode listar os grupos e ver quem está neles. Somente um admin pode criar, renomear, excluir ou alterar a associação.

Grupos estão no 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();

Direitos de acesso — a concessão é na pasta​

Esta é a frase mais importante desta página, e a que mais provavelmente vai te surpreender:

Uma concessão nomeia uma pasta, nunca um painel ou conjunto de dados individual.

Um direito de acesso é uma linha no formato:

(account, folder, resource type, permission, principal) → allow | deny
  • tipo de recurso — dataset ou viz. São os dois que uma concessão pode nomear; veja O que uma concessão pode nomear para o que governa o resto.
  • permissão — view, edit, delete, create.
  • principal — um usuário ou um grupo.
  • allow / deny — os dois tipos de acesso.

Um painel é coberto por uma concessão porque ele está na pasta concedida. Mova-o para outra pasta e ele passa a ser governado pelas concessões dessa pasta. Não há concessão por painel para perder de vista, que é a metade boa desse design; a outra metade é que a pasta é a menor unidade que você pode conceder, então uma pasta que você compartilha é uma pasta que você compartilha inteiramente.

Isso não é um teto para o quão estreito um compartilhamento pode ser​

A frase acima é sobre a concessão, não sobre o compartilhamento. Uma pasta é a menor coisa que você pode nomear em uma concessão — e você decide o que há dentro de uma pasta. Para compartilhar exatamente um painel, dê a ele uma pasta própria, o que são três chamadas que seu agente já tem:

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 é dataset ou viz na movimentação, principal_type é user ou group, e access_type é allow por padrão — deny é o outro valor, e ele vence qualquer allow.

Essas três são ferramentas MCP, e o MCP é a via suportada para isso. As rotas de access-rights são deliberadamente mantidas fora do SDK publicado e da referência de API — são encanamento de controle de acesso cuja forma não assumimos, então não existe método de cliente gerado para elas e nenhum deve ser usado. create_folder é a das três que tem equivalente no SDK, client.folders.create.

Mover um painel muda quais concessões o governam e nada mais — o painel mantém seu id, e seu link não é afetado. Portanto «compartilhar por painel é impossível» é a conclusão errada a tirar da regra da pasta: são três chamadas, e a pasta é como você expressa isso.

As duas metades de uma concessão de painel​

Um painel e os dados por baixo dele são governados por tipos de recurso separados, então um compartilhamento que nomeia apenas um deles está feito pela metade. viz:view permite que alguém abra o painel; dataset:view é o que permite que seus blocos retornem números. A próxima seção é o motivo pelo qual isso não é um detalhe que você pode adiar.

Como uma decisão é de fato tomada​

Em ordem. A primeira regra que corresponder vence:

  1. Admin → permite. Incondicionalmente, antes que qualquer concessão seja lida.
  2. O principal interno system → permite. Essa é a identidade própria de worker da plataforma (jobs de atualização, execuções agendadas); não é uma conta em que você possa fazer login.
  3. Propriedade → permite. O usuário que é proprietário da linha sempre pode lê-la e alterá-la.
  4. Caso contrário, as concessões da pasta decidem, e uma única negação vence qualquer permissão.

Vale a pena declarar com precisão que a negação vence, porque isso é mais restrito do que parece. Uma negação remove uma pasta do conjunto que foi concedido a você — ela não subtrai das regras 1 a 3. Você não pode negar um admin, e não pode negar alguém o acesso a uma linha da qual ele é proprietário. Se seu modelo mental é "negação é o botão de emergência", não é: é uma subtração apenas dentro da camada de concessão.

Dois comportamentos que vão te morder​

Concessões não se propagam para subpastas. A verificação é um teste plano de pertencimento a conjunto sobre o próprio folder_id do recurso. Pastas têm sim uma relação pai/filho, e a interface a desenha como uma árvore — mas nenhum código de autorização a percorre. Uma concessão em Marketing não alcança Marketing / Q3. Conceda cada pasta que você pretende conceder.

Uma negação em create costumava não fazer nada — corrigido em 2026-09-08. A verificação de permissão de criação lia apenas as linhas allow; o direito básico de um Criador de criar na raiz da conta ou em uma pasta da qual é proprietário, e uma linha allow explícita em outro lugar, ambos ignoravam completamente qualquer deny. Uma linha create / deny explícita de um admin agora sobrepõe as duas — a mesma regra "uma única negação vence qualquer permissão" que o resto desta página descreve para view/edit/delete. Admins continuam inegáveis, igual a qualquer outra permissão aqui.

Quem pode alterar uma concessão​

Um admin da conta, ou o proprietário da pasta onde a concessão está. Ler as concessões é aberto a qualquer membro da conta: o estado de compartilhamento é renderizado no app em páginas que qualquer membro pode abrir, então a leitura é deliberadamente irrestrita. Trate a própria lista de concessões como informação visível para toda a conta.

O que uma concessão pode nomear​

Dois tipos de recurso aceitam concessão: viz (painéis) e dataset. Construa seu modelo de governança com esses dois, guardados em pastas — é a forma que o modelo aplica e a única que o diálogo oferece. POST /v1/access-rights recusa qualquer outro tipo de recurso com um 400, no momento em que você o escreve e não no momento em que alguém conta com ele.

Três coisas são governadas por algo que não é uma concessão, e saber qual por quê é todo o sentido desta seção:

RecursoO que o governa
PastaA participação na conta para leituras; o papel para mutações. Uma pasta é o contêiner que uma concessão nomeia, não algo contra o qual uma concessão é escrita.
Programação de atualizaçãoA propriedade, mais a fronteira da conta.
PerguntaApenas o proprietário. Uma pergunta não pode ser compartilhada por nenhum mecanismo.
TarefaA ACL do painel a que pertence.
ConexãoSeu proprietário e os administradores da conta. Qualquer Creator pode criar uma.

Uma lista vazia diz qual tipo de vazio é. GET /v1/access-rights/{folder}/{type} carrega uma flag enforced, então total: 0 para um tipo não concedível significa nenhum direito desse tipo pode existir em lugar algum, enquanto total: 0 para viz significa ninguém tem um. Linhas escritas antes da recusa continuam listadas, e continuam removíveis, com enforced: false ao lado.

Compartilhar um painel sem compartilhar seus dados não funciona​

Esta é a pergunta que o modelo não responde do jeito que a maioria espera, então vale uma frase direta: viz:view sozinho não dá a ninguém um painel que funcione.

Os blocos de um painel buscam seus números com POST /v1/query, e essa rota autoriza cada conjunto de dados participante pela ACL de conjuntos de dados, no modo negar-em-vez-de-descartar: um único conjunto de dados que o leitor não pode ler rejeita a consulta inteira com 403 query_dataset_forbidden. Ela nunca é executada sobre o subconjunto que ele pode ler. Portanto, "deixe o estagiário ver este painel, mas não os dados por trás" produz um painel cujos blocos falham, não um painel com os números ocultos.

Conceda também dataset:view na pasta que contém os conjuntos de dados — viz:view e dataset:view são um par, e uma concessão que carrega apenas o primeiro produz um painel em que cada bloco dá 403. Se os conjuntos de dados não devem ser compartilhados em bloco, dê a eles uma pasta própria, do mesmo modo que a receita acima. Ou, se o objetivo for realmente mostrar um resultado sem entregar os dados subjacentes, publique o painel ou incorpore-o. Essas duas vias rodam sob a identidade de serviço da plataforma e nunca consultam esta ACL, que é exatamente o motivo pelo qual conseguem mostrar um número a alguém que não pode consultá-lo.

Conferindo o próprio trabalho​

Cada campo acima descreve uma regra; nenhum deles diz com o que uma pessoa específica termina, e isso não se lê na lista de concessões. Os direitos se compõem — participações em grupos, um deny que vence todo allow, inclusive um herdado de um grupo, a separação por tipo de recurso e o controle de papel separado sobre mutações — em um resultado que nenhuma lista isolada mostra. O deny herdado é o caso que importa: ele nunca aparece na lista de allow que você lê, então "por que esta pessoa não vê isto" fica sem resposta só com a lista.

GET /v1/access-rights/{folder}/effective?userId= resolve. Para cada par (tipo de recurso, permissão) ele devolve o que a ACL decide, qual regra decidiu, se o papel da pessoa permite, se o tipo é aplicado, o produto disso tudo — e a linha de direito de acesso que produziu o veredito, que é o id a revogar quando a resposta é um deny. Seu agente tem a mesma coisa como describe_effective_access.

Ele responde apenas à pergunta da ACL, e diz isso numa lista caveats presente em toda resposta: não enxerga as linhas que a pessoa possui (a propriedade é por linha e vence a ACL da pasta), não sabe nada sobre as vias de painel público e de incorporação e — conforme a seção acima — um painel que funciona precisa também de direitos sobre seus conjuntos de dados. Você sempre pode perguntar sobre si mesmo; perguntar sobre outra pessoa exige a mesma posição que mudar a ACL: um admin da conta ou o dono da pasta.

Alcançando isso a partir de código​

SuperfícieNos SDKs?
Grupos (criar, listar, atualizar, associação)Sim — client.groups.*
Usuários (listar, atualizar, papel, associação a grupo)Sim — client.users.*
ContaSim — client.accounts.*
Direitos de acesso (as concessões em si)Não

Direitos de acesso são excluídos da superfície gerada do SDK, então não há client.accessRights nem uma entrada para eles na Referência da API. As rotas existem e aceitam uma chave de API — elas simplesmente não fazem parte do contrato de cliente publicado e versionado, e preferimos te dizer isso do que deixar você construir contra um formato ao qual não nos comprometemos. Gerencie concessões no app, ou a partir do seu agente: o servidor MCP expõe ferramentas de conceder, revogar e listar direitos de acesso que chamam as mesmas rotas sob sua própria identidade, e herdam a mesma regra de admin-ou-proprietário-da-pasta.

Filtragem por linha: a via de incorporação, e somente a via de incorporação​

Segurança por linha é real na Vibedasher, mas ela vive em um único lugar: painéis incorporados atrás de um token assinado.

Quando seu servidor gera um token de incorporação, ele pode anexar filtros à carga do token. Em toda consulta, a plataforma reescreve cada referência a conjunto de dados em uma subconsulta filtrada, então o visualizador fisicamente não consegue ler linhas fora da sua fatia. Os filtros vêm exclusivamente do token verificado — nunca do corpo da requisição — então um visualizador não pode ampliar seu próprio escopo editando uma requisição. Um filtro que nomeia uma coluna que não existe em nenhum esquema de conjunto de dados é rejeitado com um 403 em vez de silenciosamente ignorado, porque um filtro que discretamente não faz nada é uma brecha.

Três limites a considerar:

  • Aplica-se somente à via de incorporação. A via de consulta do SDK não tem injeção obrigatória de filtro: uma chave de API é um chamador confiável do lado do servidor, e delimitar o escopo dos seus próprios usuários é trabalho seu, não da plataforma.
  • A ACL de pasta não é por linha. Ela decide quais painéis e conjuntos de dados existem para você, não quais linhas dentro deles você pode ler. Essas são perguntas diferentes, e a Vibedasher as responde com mecanismos diferentes.
  • Um painel que consulta no navegador não pode ser delimitado de forma alguma, e isso é intencional. Essa é a decisão a tomar antes de construir, não depois. Um painel auto-hospedado na via sem credenciais — a árvore ejetada cujo dataLane é wasm, ou qualquer coisa que você mesmo construiu sobre um extrato Parquet ou JSON — baixa os dados e executa toda consulta no navegador do visitante. O extrato é endereçado por conjunto de dados e snapshot, não por consulta, então ele contém todas as linhas daquele conjunto de dados, e todo visitante recebe o mesmo arquivo. Não sobra nenhum servidor no caminho para estreitá-lo: um filtro escrito na página muda o que é desenhado, nunca o que foi entregue.
O escopo por linha e a via sem credenciais se excluem mutuamente

Todo o apelo dessa via é que não há chave no bundle, nenhum token a gerar e nenhum backend seu para manter no ar — e o preço é exatamente a mesma propriedade vista do outro lado: nada pode entregar a um visualizador menos linhas do que a outro. Não é uma lacuna que será fechada; é o que significa colocar os dados no navegador.

Se alguns visualizadores precisam ver menos linhas, mantenha a consulta em um servidor. Dois formatos fazem isso, e ambos mantêm a credencial fora da página: seu próprio backend gera um token de incorporação de curta duração carregando o filtro (a plataforma acrescenta esse predicado a toda consulta que autoriza, antes de qualquer join ou agregação, de modo que a página não pode ampliá-lo), ou código de servidor confiável consulta com uma chave de API e retorna linhas que você já delimitou. Escolher primeiro a via rápida e "delimitá-la" depois com um filtro na página produz um painel que parece correto, não levanta nenhum erro, e entrega todas as linhas.

Veja Incorporação para como um token é gerado e apresentado.

Autenticação em duas etapas​

E-mail + senha é a única entrada no Vibedasher que depende de um único segredo. Os logins com Google e LinkedIn carregam o segundo fator do provedor; chaves de API e o token CLI do vibedasher login não são logins. Por isso o segundo fator é opcional, por usuário, somente no login com senha: um código TOTP de seis dígitos de qualquer aplicativo autenticador (Google Authenticator, 1Password, Authy…), ativado em Configurações → Autenticação em duas etapas escaneando um código QR e confirmando um código. Ao ativar, você recebe oito códigos de recuperação de uso único, exibidos uma só vez — um telefone perdido faz login com um deles.

O que muda no login: após uma senha correta, o navegador não recebe sessão — recebe um desafio de cinco minutos e propósito único que somente a etapa do código pode resgatar. A API nunca vê esse desafio como credencial: ele carrega uma audiência que a API recusa de imediato, então não pode ser reproduzido contra api.*.vibedasher.com nem colocado no cookie de sessão. Cinco códigos errados bloqueiam o desafio por quinze minutos; um código vale uma única vez.

Três limites para conhecer:

  • A ativação é individual. Cada membro ativa para a própria conta; não é algo que um administrador liga para todo mundo.
  • Logins OAuth nunca recebem o desafio, por decisão de projeto — colocar nosso fator em cima do fator do provedor é atrito sem segurança.
  • O desbloqueio é uma ação de administrador. Um membro sem telefone e sem código de recuperação procura um administrador da conta, que redefine o segundo fator na página do membro (a ação é registrada). Um proprietário que é o único administrador, sem código de recuperação, passa pelo suporte.

Uma configuração prática​

Para uma equipe de dez pessoas com alguns conjuntos de dados sensíveis:

  1. Torne admins a uma ou duas pessoas que administram a conta. Lembre-se de que elas são ingovernáveis por concessões — esse é o propósito do papel, e a razão para manter a lista curta.
  2. Todo mundo que constrói é um criador; todo mundo que só lê é um visualizador. O papel de visualizador é uma recusa dura em mutações, então é uma fronteira real, não uma convenção de interface.
  3. Coloque painéis e conjuntos de dados em pastas que correspondam a como você quer compartilhá-los — uma pasta por público, não por tópico. A pasta é a concessão.
  4. Conceda a grupos, não a pessoas. A concessão então sobrevive à entrada ou saída de alguém.
  5. Não crie pastas aninhadas esperando que a concessão da pasta pai as alcance. Conceda cada uma.
  6. Se um painel precisa chegar a pessoas fora da conta, publique-o e incorpore-o com um token assinado carregando os filtros de linha delas — não dê a elas uma conta.