Pular para conteúdo

0038 — Uma identidade por app: app_id == slug

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-21 · Decidido em: 2026-09-21

Contexto

A 0024 normatizou o slug como identificador público e nome da imagem, e decidiu explicitamente que ele pode divergir do app_id, com a razão registrada: "mexer nele quebra grants e identidade no broker. Logo, slug ≠ app_id por decisão, não por acidente."

A frota seguiu a norma à risca. Levantamento de 2026-09-21 mostrou dois namespaces, cada um internamente coerente — o app_id casando com o projeto GlitchTip e com db_auth.apps em todas as linhas:

slug (entrega) app_id (catálogo)
onpetro-bi bi-comercial
onpetro-xls bi-comercial-xls
vantroba-bi bi-transporte
vantroba-xls bi-transporte-xls
thoms-webstorm thoms-integracao-produto-ecom
integrador-sascar int-sascar
central-ui central-apps-auth
integrador-server integrador

O custo apareceu no uso, não na norma. Com dois nomes por app, toda investigação que atravessa sistemas — achar no Coolify o app cuja issue está no GlitchTip, ligar um alarme ao recurso de deploy — exige uma tradução mental que ninguém tem de cabeça, e a divergência é indistinguível de defeito para quem chega. Dois defeitos reais ficaram escondidos justamente por isso, porque pareciam ser mais um caso do padrão: o integrador-server apontava para projeto GlitchTip inexistente (o campo só é lido pelo job build_web, skipped nele, então falhava calado), e o rename auth → central-backend estava pela metade havia semanas.

Decisão

Uma identidade por app. O app_id do docs/app.json é igual ao slug, e esse nome é o mesmo em todos os sistemas. Isto substitui a cláusula "slug pode ≠ app_id" da 0024; todo o resto da 0024 (forma do slug, slug == nome da imagem, slug deriva o nome do recurso Coolify) segue valendo.

A invariante completa:

<identidade> == app.json slug        == app.json app_id
             == nome do repo (Forgejo e GitHub)
             == imagem do registry (fonte.xadm.biz/xadm/<id>)
             == prefixo do recurso Coolify (<id>[-<instancia>]-<alvo>-pull)
             == caminho do docs-site (docs-sites/<id>)
             == db_auth.apps.app_id  e  db_auth.apps.slug
             == slug do projeto GlitchTip
             == app_provider_resources.resource_ref.project_slug
             == features.glitchtip.project do app.json
             == o app_id que o app manda no login

Decisões de apoio:

  • A forma do nome continua a da 0024: <cliente_id>-<projeto> para app de cliente, <projeto> para app da X-Adm, kebab-case. Foi o que corrigiu ho-scraper → sulplata-ho-scraper, o único app de cliente cujo slug não levava o prefixo do cliente.
  • Os dois campos continuam existindo no app.json, agora obrigatoriamente iguais. Não se colapsa num só campo: é o par que permite a migração ser por app, e é nele que o gate bate.
  • A PASTA do repo não é identidade. O caminho na raiz multi-repo é legibilidade humana: clientes/onpetro/bi-comercial se lê melhor que clientes/onpetro/onpetro-bi, porque o caminho já carrega o cliente. A ligação pasta↔identidade é o docs/app.json, e só ele. Medido: renomear as pastas custaria ~350 referências reais em etc/tests (compose dos e2e, scripts .ps1, allowlists de .claude/settings.json) para ganho funcional zero.
  • A URL pública não é identidade. <função>.<cliente>.xadm.biz é para humano digitar; excel.onpetro.xadm.biz servindo o app onpetro-xls está correto.

Como migrar sem quebrar — a razão da 0024 era real

A 0024 dizia que mexer no app_id quebra grants. Está certa, e só para uma classe de app: o app que manda o próprio app_id no login, compilado no bundle.

// clientes/onpetro/bi-comercial/lib/core/config/app_config.dart:122
appId: ouPadrao(const String.fromEnvironment('APP_ID'), 'bi-comercial'),
// .../features/000_login/services/social_auth_service.dart:188
body: jsonEncode({'cliente': cfg.authTenant, 'app_id': cfg.appId}),

Renomear a linha do catálogo sem reconstruir o app faz o login mandar um id fora do catálogo: o claim oauth_app_id deixa de ser emitido e o acesso escopado cai.

Só 2 dos 11 apps estão nessa classe (os dois BIs Flutter). Os outros 9 têm app_id apenas como dado de catálogo — convergem com um UPDATE e um rename de projeto, sem release. Levantado por grep -rl app_id src/main lib: devolve 0 em integrador-server, int-sascar, os dois xls, maxsul-pied e thoms-webstorm; e no central-ui as 8 ocorrências são todas de consumo de app_id vindo da API, nunca a própria identidade.

Para os 2 que mandam, a migração é por app, na release do próprio app, com coexistência das duas linhas para janela zero e sem tocar no central-backend:

  1. app_id novo no app.json — e garantir que o build injeta APP_ID (o bi-transporte injeta, Dockerfile:65; o bi-comercial não, então hoje ignora o app.json e usa o default compilado — corrigir isso primeiro, senão o app.json mente);
  2. inserir a linha nova no catálogo mantendo a antiga, duplicando app_roles e app_user_access nas duas (o bi-comercial tem 4 papéis e 10 acessos);
  3. release do app;
  4. remover a linha antiga, renomear o projeto GlitchTip e atualizar o resource_ref.

O que NÃO acompanha o rename automaticamente

Duas linhas da invariante não se movem sozinhas, e são a fonte das armadilhas:

  • app_provider_resources.resource_ref.project_slug — é dele, e não do app_id, que o central-ui monta o link do GlitchTip (AppCatalogService). Renomear o projeto e atualizar o ref são um passo atômico. Confirmado na prática durante a execução: na janela entre os dois, um script de monitoração que consultava /projects/x-adm/integrador/issues/ passou a devolver vazio, em silêncio — sem erro, o que é o pior modo de falha possível.
  • o app_id compilado no bundle — ver acima.

E um sistema fica deliberadamente fora: o Aptabase. O resource_ref.aptabase_app guarda o nome do app no SaaS externo; ele só muda quando for renomeado lá, e até então o valor antigo é o correto em relação àquele sistema.

Consequências

  • Rename de projeto GlitchTip é barato e não perde evento: o DSN referencia o id numérico (bug.xadm.biz/16), não o slug. Verificado: histórico das issues intacto nos 7 projetos renomeados.
  • Nada no registry mudou: as 10 imagens já eram o slug canônico, então a convergência não custou rebuild, retag nem recurso Coolify de app.
  • O deploy não é afetado por app_id: o POST /api/ci/deploy (0026) recebe um campo chamado app_id mas o usa como registry_slug, sem consultar a tabela apps. Com uma identidade só a ambiguidade desaparece; o nome do campo deve ser corrigido para registry_slug (hoje o próprio javadoc admite: "app_id (== registry_slug do reconcile)").
  • Gate obrigatório, senão a convenção volta a ser só convenção: o /xadm-docs confere a invariante, e em especial features.glitchtip.project == app_id == slug. É a checagem que pegaria sozinha os dois defeitos que motivaram esta decisão.
  • Estado em 2026-09-21: 9 dos 11 apps convergidos (catálogo, GlitchTip e app.json). Faltam bi-comercial → onpetro-bi e bi-transporte → vantroba-bi, cada um na release do seu app.