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, projeto000-compartilhadono Coolify). O acesso é pelo terminal do recurso do PostgreSQL dentro do Coolify: lá,psqlentra no banco comopostgres(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>) ecliente_idna 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 comCREATE 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).
- Crie o repo a partir de um cliente existente — ex.:
maxsul-powersync. Nodocker-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. - Ajuste o
powersync.yaml: - sync rules — as tabelas do cenário do cliente (ver cenário 1 §Tabelas que sincronizam e cenário 3);
client_auth— quem conecta: chave pública dointegrador-client(cenário 3), chave da Central (apps com login X-Adm) ou Firebase (apps com login Google). Norma em PowerSync.- Espelhe o repo no GitHub e cadastre o secret
CENTRAL_DEPLOY_TOKEN— é o que permite ao pipeline publicar. -
Crie o recurso no Coolify —
+ New Resource→ Docker Compose a partir do git (branchmaster,docker-compose.yamlna raiz), domíniohttps://ps.<cliente>.xadm.bizpela variávelSERVICE_FQDN_POWERSYNC, e as variáveis:Variável Valor PS_DATA_SOURCE_URIpostgresql://powersync_role:<senha>@<host>:5432/db_<cliente>— formato URI, não JDBC;@na senha vira%40PS_MONGO_URImongodb://<usuario>:<senha>@mongo-<cliente>:27017/powersync_<cliente>?authSource=admin&directConnection=trueMONGO_ROOT_USER/MONGO_ROOT_PASSWORDos mesmos da PS_MONGO_URI -
Publique pelo pipeline do repo (push com gate verde). A configuração é embutida na imagem: toda mudança no
powersync.yamlexige 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) |