Runbook — semear o catálogo de papéis de um app (app_roles)¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-04
O catálogo de papéis por app é data-driven: a migration V12 cria a tabela app_roles vazia
(migration é schema, não dado). Cada app precisa ter os papéis cadastrados antes do 1º approve
daquele app — senão o approve responde 422 role_not_in_catalog.
Pré-condição: o app precisa existir no catálogo apps¶
app_roles.app_id tem FK → apps(app_id). A linha apps nasce da catalogação da Central de Apps
(POST /api/setup/provision a partir do app.json, ou backfill). Confirme:
SELECT app_id FROM apps WHERE app_id = 'bi-comercial';
Se não existir, catalogue o app primeiro (fluxo /xadm-setup / broker) — não insira papéis antes.
Semear pelo que o app declara (caminho preferido)¶
Quem sabe quais papéis existem é o app — é o código dele que faz o gate. Ele publica a lista em
GET <base>/roles-catalog.json e o central ingere
(decisão 0025):
curl -sS -X POST "https://central-backend.xadm.biz/api/admin/apps/bi-comercial/roles/sync" \
-H "Authorization: Bearer $XADM_JWT" -H "Content-Type: application/json" -d '{}'
# {"app_id":"bi-comercial","base_url":"https://bi.sulplata.xadm.biz",
# "declarados":4,"criados":4,"atualizados":0,"roles":["ADMIN","COMERCIAL","DIRETORIA","LUBRIFICANTE"]}
- Idempotente: rodar de novo devolve
criados:0. Rode sempre que o app ganhar um papel novo — é o passo que fecha o drift entre o gate do app e o picker do central. - Não deleta. Papel que sumiu da declaração fica no catálogo (apagá-lo órfãozaria os grants que já
o referenciam). Remoção é o
DELETEmanual abaixo, depois de tratar os grants. - Sem
production_urlalcançável (e2e local), passe-d '{"base_url":"http://localhost:8101"}'. 502 roles_catalog_unreachable→ o app não respondeu ou devolveu JSON inválido;422→ obase_urlnão é http(s) ou o app declarou um papel maior que a coluna.
Semear papel a papel (saída manual)¶
Quando o app ainda não serve o roles-catalog.json, ou para corrigir um caso pontual.
Com um JWT xadm (xadm_admin=true):
for r in ADMIN:Administrador DIRETORIA:Diretoria COMERCIAL:Comercial LUBRIFICANTE:Lubrificante; do
role="${r%%:*}"; label="${r##*:}"
curl -sS -X POST "https://central-backend.xadm.biz/api/admin/apps/bi-comercial/roles" \
-H "Authorization: Bearer $XADM_JWT" -H "Content-Type: application/json" \
-d "{\"role\":\"$role\",\"label\":\"$label\"}"
done
roleé gravado em UPPERCASE (machine-code; vai verbatim no claimroledo JWT e é o valor que a sync rule do PowerSync compara).409 role_exists→ o papel já existe (idempotente na prática; ignore).- Este é o caminho que drifta: o app ganha um papel, ninguém repete o
POSTaqui, e o papel não aparece no picker — sem erro nenhum. Prefira osyncacima.
Conferir: GET /api/admin/apps/{app}/roles (aqui {app} = bi-comercial) ou
SELECT role, label FROM app_roles WHERE app_id = 'bi-comercial' ORDER BY role;
Papéis do bi-comercial (referência)¶
| role | label | significado |
|---|---|---|
ADMIN |
Administrador | vê tudo + administra usuários do app |
DIRETORIA |
Diretoria | vê tudo, não administra usuários |
COMERCIAL |
Comercial | operacional (recorte comercial na sync rule) |
LUBRIFICANTE |
Lubrificante | operacional (só lubrificante na sync rule) |
pAbast intocado. Esta feature é aditiva: usuários que logam via pAbast seguem inalterados; o catálogo/aprovação só governa o acesso social (
oauth_firebase).