Pular para conteúdo

0021 — Adoção do control-plane de deploy com resolução ancorada na imagem

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-08-30 · Decidido em: 2026-08-30

Contexto

A casa ratificou o control-plane de deploy no central-backend (ADR central 0026 + constituição §9): o CI dispara o deploy por API M2M, o central resolve app → recurso Coolify por um mapa auto-curável e chama a API do Coolify — matando a ponte SSH + o allow-list órfão. Esta RD registra a adoção local (Fase 2, a implementação) — não re-litiga a decisão (é da casa; o o quê e o porquê duráveis vivem na 0026 e em infraestrutura/deploy.md, §1.9).

Agravante local: a decisão 0020 renomeou auth → central-backend no app.json, mas o catálogo apps seguiu com app_id="auth" — nada no catálogo (app_id, slug, production_url=auth.xadm.biz) casa com o app.json (central-backend). Resolver deploy por app_id quebraria no rename, e o Gustavo exigiu que a resolução se auto-adapte a rebrand (o foco do app muda com o tempo).

Decisão

A resolução de deploy ancora no SLUG DE REGISTRY DA IMAGEM (docker_registry_image_name), não no app_id. O que o CI builda (fonte.xadm.biz/xadm/<slug>:<target>-amd64) e o Coolify puxa é o mesmo slug — e esse slug espelha o app.json corrente, imune ao catálogo stale. Rebrand futuro flui sozinho.

  • POST /api/ci/deploy {app_id, target, image, instance?} — image obrigatório (a âncora); app_id é rótulo/auditoria. Filtro próprio @ServerFilter("/api/ci/**") valida CENTRAL_DEPLOY_TOKEN (molde do SetupAuthFilter, não o AdminAuthFilter tudo-ou-nada).
  • V10 app_deploy_targets (registry_slug, target, instance, coolify_uuid, fqdn), unique (registry_slug, target, coalesce(instance,'')), sem FK a apps (desacopla do catálogo stale) + deploy_audit (rastro por recurso; token_ref = sha256:+12hex, nunca cru).
  • Reconcile-from-Coolify ancorado na imagem: deriva registry_slug (image name), target (tag), instance do FQDN (int.<cli>.xadm.biz→<cli>; os 6 integradores compartilham o slug, só o fqdn os separa). Cadência on-boot + on-demand + on-miss com debounce. Auto-curável (blue-green/rebrand re-ancoram sozinhos).
  • GET /api/ci/deploy/{deployment_uuid} — status degradável. ~~Guarda bootstrap: deploy do próprio central via o endpoint é recusado~~ → self-deploy do central agora é PERMITIDO (0022): a guarda self_deploy_refused foi removida (a "API direta" era inexequível do runner GitHub). O central se deploya pelo próprio /api/ci/deploy.

Alternativas descartadas

  • Chavear no apps.app_id — stale após 0020, quebra no rebrand (o oposto do exigido).
  • Casar por fqdn/production_url — também divergiu no catálogo (auth.xadm.biz); menos estável que o slug da imagem.
  • Rename manual do catálogo (auth→central-backend) como pré-requisito — não-adaptativo, repete no próximo rebrand. Fica como higiene separada, não-bloqueante.

Consequências

  • image vira âncora de resolução, não só auditoria como a 0026/deploy.md (0.37.3) dizem — pendência de clarificar a doc central + patch bump (cross-repo).
  • Convenção de status HTTP dos endpoints M2M (sem-recurso 404, tudo-falhou 502, parcial/ok 200) decidida por analogia com o broker de provisão — candidato a normatizar na casa. (O self-deploy 409 saiu — revertido pela 0022.)
  • Higiene do catálogo apps (app_id="auth"→central-backend, fechando o 0020) fica como follow-up não-bloqueante (o deploy resolve por slug/imagem).
  • Reuso: CoolifyClient (deploy() + listApplications com image), SetupAuthFilter (molde), Flyway.