Permissões e escopos
16 permissões nomeadas, e não um bitfield. Para um App, 11 delas podem ser autorizadas numa instalação — e o que ele de fato pode é a interseção dos cargos com esses escopos.
Nomes, não bits
O vocabulário é fechado e viaja como texto. Um cargo carrega "permissions": ["gerenciar_canais", "criar_convite"], e é isso que você lê no retrato do servidor e no evento de cargo. Não há máscara para montar nem potência de dois para somar.
Os nomes são as únicas palavras em português que atravessam a fronteira, e a razão é simples: eles são gravados no banco, aparecem na tela de autorização que uma pessoa lê e são o mesmo texto nos dois lados. Traduzi-los na borda criaria duas nomenclaturas para manter.
O vocabulário
| Permissão | Instalável | O que ela permite |
|---|---|---|
administrador | sim | Tudo, sem exceção. Só o dono do servidor consegue conceder. |
gerenciar_cargos | sim | Criar, editar, apagar e reordenar cargos — dentro do próprio alcance. |
gerenciar_canais | sim | Criar, editar, mover entre categorias e arquivar canais. |
gerenciar_mensagens | sim | Apagar mensagem de qualquer pessoa no servidor. |
publicar_avisos | sim | Publicar comunicado do servidor. |
mencionar_todos | sim | Usar @everyone e @here. É permissão própria porque chamar o servidor inteiro é barulho que não se desfaz. |
criar_convite | sim | Gerar link de convite. |
gerenciar_convites | sim | Ver e revogar os convites que existem. |
gerenciar_servidor | sim | Nome, ícone, banner, categorias, descoberta e ordenação. |
gerenciar_apps | sim | Autorizar App, ajustar o que ele pode pedir e removê-lo. |
expulsar_membros | não | Tirar alguém do servidor. |
banir_membros | não | Barrar a volta de alguém. |
desconectar_de_chamada | não | Tirar alguém de um canal de voz. |
mutar_microfone | não | Silenciar o microfone de alguém em chamada. |
mutar_audio | não | Ensurdecer alguém em chamada. |
ver_registro | sim | Ler o registro de auditoria do servidor. |
Por que a moderação fica de fora
As 5 permissões marcadas como não instaláveis são as de agir sobre pessoas: expulsar_membros, banir_membros, desconectar_de_chamada, mutar_microfone, mutar_audio. Elas não são escopo que uma instalação possa autorizar. Um cargo ainda pode carregá-las, e a interseção as zera — conceder o que não produz nada é armadilha; deixar de fora é honesto. O motivo completo, junto das outras três fronteiras, está em O que um App nunca alcança.
Escopo, cargo, e a regra que os junta
Um App instalado tem duas coisas, e elas não são a mesma:
- Escopos — o teto da instalação. É o que quem autorizou disse que este App pode chegar a ter naquele servidor.
- Cargos — o que ele de fato recebeu, como qualquer membro recebe.
capacidade = permissões dos cargos ∩ escopos autorizadosO cargo automático
Toda instalação tem um cargo próprio, que nasce e morre com ela. Ele não é editável pelas rotas de cargo (a recusa é APP_ROLE_IMMUTABLE) — o que se ajusta é o conjunto de escopos, na tela de autorização.
Hierarquia
- Posto é separado de permissão. O dono está acima de tudo; depois vem a posição do cargo mais alto de cada um.
- O posto de um App é o do cargo mais alto que ele tiver, como o de qualquer membro — não existe posto fixo. Quem impede a escalada não é a posição, é o teto: sem o escopo autorizado na instalação, cargo nenhum, por mais alto, produz capacidade. Quando a capacidade existe nos dois eixos, a hierarquia vale como para gente — e chegar lá exige duas decisões humanas conscientes: alguém autorizou o escopo e alguém deu o cargo.
- Ninguém concede o que não tem. Uma tentativa de dar mais do que o próprio alcance volta como
CANNOT_GRANT_PERMISSION. - Só o dono liga
administrador. Vale para ligar a permissão num cargo e para atribuir um cargo que já a tem.
O canal, que é o terceiro eixo
As permissões acima valem no servidor inteiro. Dentro de um canal existe um eixo próprio, e ele alcança o seu App do mesmo jeito que alcança gente — quer dizer: um App instalado pode não ver um canal, e pode ver e não poder escrever nele. São duas perguntas separadas, com respostas separadas.
Visibilidade: uma lista de quem alcança
Um canal restrito não é "todo mundo menos alguns": é uma lista aditiva de contas e cargos que o alcançam. Quem não está nela não vê o canal, e para o seu App isso tem duas consequências que valem mais do que qualquer parágrafo:
- O Gateway não entrega o que o App não vê. O corte é o mesmo funil que vale para gente, e ele acompanha a mudança sem reconexão: quando o App perde o alcance de um canal, os eventos daquele canal param de chegar no fluxo que já estava aberto.
- Na REST, a resposta é
CHANNEL_NOT_FOUND, e não uma recusa de permissão. É o mesmo 404 de canal inexistente, de propósito: distinguir contaria a quem sonda que existe um canal ali.
Quem dá alcance a um App é quem administra o servidor, pelos cargos da instalação ou nomeando o App direto. Não há gesto seu nisso, e não há rota para o App pedir alcance.
Capacidades: o que dá para fazer lá dentro
Visto o canal, cada gesto passa por uma capacidade própria dele — ler, enviar, anexar, reagir, mencionar_todos, gerenciar_mensagens nos canais de texto; entrar, falar, video e transmitir_tela nos de voz. A recusa aqui é MISSING_PERMISSION, com 403: o canal existe, e o gesto é que não passa.
Sem ninguém ter escrito nada sobre aquele canal, as capacidades de membro comum respondem sim — falar continua sendo o piso de quem está lá dentro. O que as tira é um deny escrito, no canal ou na categoria que o contém; o do canal vence o da categoria.
Ver o canal e ler nele são duas perguntas
Duas delas decidem o que o gateway entrega, cada uma na sua superfície: ler nos canais de texto e entrar nos de voz. Negada a capacidade da superfície, o canal continua existindo para o seu App — ele segue na lista e segue mandando channel_update e channel_delete —, e nada do que acontece dentro chega: mensagem, reação, digitação e, numa sala de voz, a ocupação.
Cada canal do retrato traz canRead e canSend: a resposta por canal, antes de tentar. canRead: false com canSend: true é configuração legítima — a caixa de sugestões: o App posta e não recebe nem o próprio eco.
const { channels } = await app.rest.getServer({ serverId });
const mudos = channels.filter((canal) => !canal.canRead);
const restritos = channels.filter((canal) => canal.visibility === "restricted");A lista que volta já é só o que o App alcança: um canal que ele nem vê não aparece nela. Um bot mudo num canal específico quase sempre é isto, e não bug.
Uma diferença que quem vem de fora não espera: administrador não é curinga dentro do canal. Ele concede tudo no degrau do servidor e não fura um deny de canal — é o que faz canal restrito significar alguma coisa.
Falar não está nesta lista
Não existe permissão de "enviar mensagens": falar é o piso de quem é membro. O equivalente para o App é estar instalado. A consequência é que um App com o conjunto de escopos vazio ainda escreve — e quem instala já é quem tem gerenciar_apps, com o gesto de desinstalar à mão, que cala na hora.
O que não existe
Não há escopo OAuth de usuário — nem consentimento individual, nem redirect URI: o App age sempre como ele mesmo. E não há escopo por canal: o teto da instalação é do servidor inteiro. O que existe por canal é o eixo da seção acima, e ele só TIRA — nenhum override amplia um App além dos escopos autorizados. O resto do que não existe, e por quê, está em O que um App nunca alcança.