Pular para conteúdo

Cliente novo na nuvem

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

Aplica-se a: quem opera a nuvem (acesso ao Coolify, ao Postgres e aos repos).

O que é

O checklist único para deixar um cliente pronto na nuvem: projeto no Coolify, banco, integrador (int.<cliente>.xadm.biz), sincronização (ps.<cliente>.xadm.biz), cadastro na Central e as credenciais que vão para o cliente. Cada passo aponta para o runbook detalhado do repo dono — aqui fica a ordem e o que tem de casar entre os passos.

Quando usar

  • Cliente que vai usar qualquer um dos três cenários e ainda não tem banco nem PowerSync.
  • Cliente que já tem a base e ganha o cenário 1 ou o cenário 3 — pule para o passo que falta (normalmente o 4).

Cliente que só usa o cenário 2 (relatórios) não precisa do integrador (passo 4): o app de planilha grava direto no banco.

Pré-requisitos

  • Acesso ao Coolify (admin.xadm.biz) — hoje só o Gustavo tem login.
  • Acesso ao Postgres compartilhado (postgresql18, projeto 000-compartilhado no Coolify). O acesso é pelo terminal do recurso do PostgreSQL dentro do Coolify: lá, psql entra no banco como postgres (superusuário).
  • Acesso de escrita ao Forgejo (fonte.xadm.biz/xadm/) e ao GitHub (onde rodam os pipelines). Hoje só o Gustavo tem login no Forgejo.
  • O slug do cliente decidido: minúsculo, sem espaço, sem acento (maxsul, onpetrotrading). Ele vira endereço (int.<cliente>.xadm.biz), nome de banco (db_<cliente>) e cliente_id na Central — o mesmo valor em todo lugar.

DNS não é pré-requisito. A zona xadm.biz tem o curinga *.xadm.biz: qualquer int.<cliente> ou ps.<cliente> já resolve para a nuvem. Não crie registro por cliente.

Passos

1. Projeto no Coolify

Crie um projeto NNN-<cliente> (próximo número livre — ex.: 005-maxsul, 006-thoms), ambiente production. Todos os recursos do cliente (integrador, apps, PowerSync) ficam nele. O banco não fica nele: fica no Postgres compartilhado.

2. Banco do cliente

No terminal do postgresql18 no Coolify, abra o psql e crie o banco e o usuário da aplicação:

CREATE DATABASE db_<cliente>;
CREATE USER user_<cliente> WITH PASSWORD '<senha-forte>';
GRANT ALL PRIVILEGES ON DATABASE db_<cliente> TO user_<cliente>;
\c db_<cliente>
GRANT ALL ON SCHEMA public TO user_<cliente>;

O user_<cliente> cria e altera as tabelas (as migrations rodam sozinhas quando o integrador ou o app sobe). Não crie tabela à mão. O integrador-server é dono das migrations que espelham o X-Adm; os outros apps têm as suas próprias migrations, que rodam no mesmo banco do cliente, sempre com o prefixo do seu escopo nas tabelas (o app de planilha usa bi_*, o PIED usa pied_*, e assim por diante).

Depois, a leitura do PowerSync e a publicação. O usuário de leitura powersync_role já existe no servidor (usuário do Postgres vale para todos os bancos) — para o cliente novo basta liberar o banco dele:

\c db_<cliente>
GRANT CONNECT ON DATABASE db_<cliente> TO powersync_role;
GRANT USAGE ON SCHEMA public TO powersync_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO powersync_role;
ALTER DEFAULT PRIVILEGES FOR ROLE user_<cliente> IN SCHEMA public
  GRANT SELECT ON TABLES TO powersync_role;   -- tabelas que as migrations criarem depois
CREATE PUBLICATION powersync FOR ALL TABLES;   -- o nome "powersync" é fixo
  • A replicação lógica (wal_level=logical) já está ligada no servidor — não mexa.
  • Servidor novo, sem o powersync_role: criar uma vez com CREATE ROLE powersync_role WITH REPLICATION BYPASSRLS LOGIN PASSWORD '<senha-forte>';.
  • FOR ALL TABLES é o padrão da casa: tabela nova entra sozinha.

Detalhe e armadilhas: Coolify §Banco no PostgreSQL compartilhado e PostgreSQL para PowerSync.

3. Gerar os tokens do cliente

Gere cada token com openssl rand -hex 32 e guarde só nas variáveis do Coolify (e, quando for para o cliente, entregue por canal seguro). O nome de cada variável é o do serviço que recebe a chamada.

Token Para quê Onde é configurado Quem mais precisa dele
INTEGRADOR_API_TOKEN autoriza tudo que chama o int.<cliente> env do integrador (passo 4) agente de envio do X-Adm (cenário 1), integrador-client (cenário 3), apps da nuvem que gravam no integrador (pied, webstorm)
token do app de planilha (ex.: BI_COMERCIAL_XLS_API_TOKEN, VANTROBA_XLS_API_TOKEN) autoriza o envio de relatórios ao excel.<cliente> env do app de planilha a tarefa do X-Adm que envia os relatórios (cenário 2)
senha do powersync_role (já existe, é a mesma para todos os clientes — não gerar) PowerSync lendo o banco PS_DATA_SOURCE_URI do PowerSync (passo 5) ninguém fora da nuvem
senha do Mongo armazenamento interno do PowerSync MONGO_ROOT_PASSWORD e PS_MONGO_URI (passo 5) ninguém fora da nuvem

Os tokens nunca se repetem entre clientes, e o valor é o mesmo em quem valida e em todos os que chamam. Troca de token = trocar nos dois lados no mesmo momento.

4. Integrador — int.<cliente>.xadm.biz (cenários 1 e 3)

No projeto do cliente, + New Resource → Docker Image:

Campo Valor
Imagem fonte.xadm.biz/xadm/integrador-server:native-amd64
Nome do recurso integrador-server-<cliente>-native-pull
Domínio (o primeiro da lista) https://int.<cliente>.xadm.biz
Porta 8080
Health check GET /health
Auto-deploy desligado — quem publica versão nova é o pipeline do repo

Variáveis obrigatórias:

Variável Valor
CLIENTE nome de exibição do cliente
DATASOURCES_DEFAULT_URL jdbc:postgresql://<host-do-postgresql18>:5432/db_<cliente>
DATASOURCES_DEFAULT_USERNAME / _PASSWORD user_<cliente> e a senha do passo 2
INTEGRADOR_API_TOKEN o token do passo 3 — sem ele o app não sobe
SENTRY_DSN / SENTRY_ENVIRONMENT DSN do projeto integrador-server no GlitchTip (o mesmo para todos os clientes) e production
CLIENTE_ID / CENTRAL_API_TOKEN só se o cliente terá integrador-client (cenário 3): o slug do cliente e o token da Central

Deploy: Redeploy no recurso. Na primeira subida as migrations criam todas as tabelas-espelho — acompanhe o log. Depois, acrescente https://int.<cliente>.xadm.biz em smoke.bases no docs/app.json do integrador-server, para o teste pós-deploy passar a cobrir o cliente.

Uma imagem serve todos os clientes: cada versão nova do integrador é publicada em todas as instâncias de uma vez, e o recurso novo é encontrado sozinho pelo domínio int.<cliente>.

Runbook completo, com a troca jar → native e a reversão: Implantação do integrador no Coolify.

5. PowerSync — ps.<cliente>.xadm.biz

Cada cliente tem o seu repo de configuração <cliente>-powersync (fonte.xadm.biz/xadm/), com a lista de tabelas que sincronizam (as sync rules, no powersync.yaml) e quem pode se conectar (client_auth).

  1. Crie o repo a partir de um cliente existente — ex.: maxsul-powersync. No docker-compose.yaml, troque o nome do cliente (mongo-maxsul, powersync-maxsul-app, comentários). Roteamento não se configura ali: domínio e porta o Coolify gera sozinho — não acrescente label do Traefik com o identificador do recurso.
  2. Ajuste o powersync.yaml:
  3. sync rules — as tabelas do cenário do cliente (ver cenário 1 §Tabelas que sincronizam e cenário 3);
  4. client_auth — quem conecta: chave pública do integrador-client (cenário 3), chave da Central (apps com login X-Adm) ou Firebase (apps com login Google). Norma em PowerSync.
  5. Espelhe o repo no GitHub e cadastre o secret CENTRAL_DEPLOY_TOKEN — é o que permite ao pipeline publicar.
  6. Crie o recurso no Coolify — + New Resource → Docker Compose a partir do git (branch master, docker-compose.yaml na raiz), domínio https://ps.<cliente>.xadm.biz pela variável SERVICE_FQDN_POWERSYNC, e as variáveis:

    Variável Valor
    PS_DATA_SOURCE_URI postgresql://powersync_role:<senha>@<host>:5432/db_<cliente> — formato URI, não JDBC; @ na senha vira %40
    PS_MONGO_URI mongodb://<usuario>:<senha>@mongo-<cliente>:27017/powersync_<cliente>?authSource=admin&directConnection=true
    MONGO_ROOT_USER / MONGO_ROOT_PASSWORD os mesmos da PS_MONGO_URI
  7. Publique pelo pipeline do repo (push com gate verde). A configuração é embutida na imagem: toda mudança no powersync.yaml exige nova publicação, e mudar as sync rules faz todos os aparelhos baixarem tudo de novo.

Nunca rode docker compose down -v no PowerSync

O -v apaga os volumes do Mongo: todos os apps e o integrador-client do cliente perdem o estado de sincronização e baixam tudo de novo.

Runbook: Operação do PowerSync · Validar as sync rules.

6. Cadastro na Central

Cadastre o cliente na Central — é o que permite ao integrador-client mandar sinal de vida e aparecer na aba Deploy do painel (central.xadm.biz):

curl -sf -X POST https://central-backend.xadm.biz/api/admin/clients \
  -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{ "cliente_id": "<cliente>", "audience": "powersync-<cliente>", "auth_methods": ["pabast"] }'

Runbook: Provisionar banco e clientes na Central.

O cadastro também pode ser feito pela Central UI, em https://central.xadm.biz — exige conta Google com e-mail @xadm.com.br.

7. Pacote para o cliente

O que sai da nuvem para o servidor do cliente depende do cenário:

Cenário O cliente recebe
1 — tempo real endereço https://int.<cliente>.xadm.biz/api/v1/xadm + INTEGRADOR_API_TOKEN (para o agente de envio da equipe X-Adm)
2 — relatórios endereço https://excel.<cliente>.xadm.biz/api/xls/processar + token do app de planilha (ou, no modo antigo, o acesso SSH)
3 — entrada pacote do integrador-client: .jar, integrador-client.properties preenchido e a chave privada <cliente>-private.pem (cenário 3 §Instalar)

Nenhum certificado de site é instalado no cliente: o HTTPS é emitido pela nuvem.

Verificação

Conferir Como Esperado
Integrador de pé curl https://int.<cliente>.xadm.biz/health 200 com "status":"UP"
Token do integrador curl -X PUT https://int.<cliente>.xadm.biz/api/v1/xadm -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{"Origem":"TESTE"}' 200 com "resultado":"ERRO" e a mensagem payload sem nenhuma tabela reconhecida — prova que o token passou sem gravar dado (fica só uma linha de auditoria em request). Sem token: 401
Tabelas criadas \dt no db_<cliente> as tabelas-espelho (contratos, estoque, itensped…) e flyway_schema_history
PowerSync lendo o banco log do serviço powersync no Coolify sem PSYNC_S1105/erro de conexão; replicação iniciada
Publicação SELECT pubname, puballtables FROM pg_publication; powersync, t

Pares que têm de casar

A maior parte dos problemas de implantação é um par que não casa. Confira todos antes de chamar o cliente.

De um lado Do outro Sintoma se não casar
INTEGRADOR_API_TOKEN no integrador token no agente do X-Adm, no integrador-client (powersync.upload.token) e nos apps que gravam no integrador 401 em todas as chamadas
senha do powersync_role PS_DATA_SOURCE_URI PowerSync não replica
MONGO_ROOT_PASSWORD PS_MONGO_URI PowerSync não sobe
chave privada .pem do integrador-client chave pública (JWK) no powersync.yaml, mesmo kid e audience 401 PSYNC_S2101 no integrador-client
cliente_id na Central CLIENTE_ID no integrador sinal de vida rejeitado (422)