Versionamento e estabilidade
O que está congelado por teste de contrato na API PushMesh, o que pode mudar sem aviso, e como saber qual versão está no ar — sem confundir a versão do serviço com a do SDK.
Versionamento e estabilidade
A pergunta que importa quando você confia a sua base de usuários a uma API não é “qual a versão?”, é “o que aqui pode mudar debaixo de mim?”. Esta página responde exatamente isso, e separa o que é promessa do que é detalhe de implementação.
A versão está no caminho
Todo o contrato público vive sob /api/v1. Dentro da v1, as mudanças são
aditivas: campos novos podem aparecer nas respostas, campos existentes não
somem nem mudam de tipo. Uma quebra de contrato exigiria um caminho novo.
Para saber o que está no ar:
curl https://api.pushmesh.io/
{ "servico": "pushmesh", "versao": "0.1.0", "git": "unknown" }
Três leituras honestas desse retorno:
versaoé a versão do SERVIÇO. Ela vem do pacote do serviço e sobe a cada publicação. No momento em que esta página foi escrita, a instância pública respondia0.1.0.- A versão do SDK é outra coisa, com numeração própria. O pacote React
Native
@pushmesh/sdkestá hoje em0.7.2. Os dois números não andam juntos e nunca vão coincidir por acaso — não deduza um a partir do outro. gitpode vir"unknown". Ele carrega o commit do build quando a instância foi compilada com essa informação injetada; quando não foi, o campo existe e vale"unknown". É diagnóstico, não contrato: não escreva lógica em cima dele.
O que é contrato aqui: o caminho /api/v1 e o fato de a raiz responder um
JSON com esses três campos.
O que está congelado por teste de contrato
O projeto guarda respostas-modelo de chamadas reais, e a suíte de testes as compara byte a byte. Mudar qualquer um desses formatos quebra o teste antes de chegar em produção — é isso que transforma “compatibilidade” de intenção em garantia mecânica.
Estão congelados assim:
| O que está congelado |
|---|
| Resposta de sucesso do envio |
| Resposta de repetição idempotente |
Envio parcial (errors como objeto) |
Ninguém alcançável (200 com id vazio) |
| 422 de chave de idempotência reusada |
| Consulta de um disparo |
Consulta no plano gratuito (com o bloco recebidos_gate) |
| Lista de disparos |
| Corpo enviado ao provedor Android |
| Corpo enviado ao provedor iOS |
| Resposta do registro de aparelho |
Além dos formatos de resposta, estão travados por teste:
Os tetos publicados. Os números de requisições por segundo têm um teste
próprio, com a justificativa escrita junto: se alguém mudar isso sem querer, o
cliente que integrou pelo número publicado começa a tomar 429 sem explicação.
A assinatura dos webhooks. Vetor fixo: prefixo sha256=, 64 caracteres
hexadecimais, determinística, e segredo diferente produz assinatura diferente. Se
ela mudasse, todo endpoint de cliente passaria a rejeitar as entregas como
falsas.
A curva de reenvio dos webhooks. 1 s → 10 s → 60 s → 5 min → 15 min, com teto.
A serialização canônica do corpo de envio. É ela que gera o request_hash da
idempotência: chaves de objeto ordenadas recursivamente, arrays na ordem, números
como vieram, send_after nunca reinterpretado. Mudar essa função faria todo
cliente tomar 422 no retry de uma chave já usada.
As strings assinadas das provas. A prova de recibo de push e a de exibição In-App têm o texto exato da assinatura congelado no contrato — o SDK só ecoa, nunca calcula:
pm_rcpt = hex(HMAC-SHA256(receipt_key, "<notification_id>:<device_uuid>"))[..32]
pm_iam = hex(HMAC-SHA256(receipt_key, "iam:<campanha_id>:<device_uuid>:<pacote_id>"))[..32]
O prefixo iam: é o que separa os dois universos: um recibo de push nunca vale
como prova de In-App, e vice-versa, mesmo com a mesma chave.
O tamanho medido do payload. O orçamento de 3.891 bytes e os 96 bytes que o servidor injeta são conferidos por teste contra o render real — a porta não pode “esquecer” bytes que ela mesma vai acrescentar depois.
O formato dos parâmetros públicos do Firebase. android vem null quando
não configurado e ios é campo reservado, sempre null nesta fase — o contrato
nasce estável para o SDK não mudar quando o iOS chegar.
O que pode mudar sem aviso
Seja explícito com o seu código sobre estas três coisas, porque elas não são contrato:
- O texto de
explain.causaeexplain.como_corrigir. Eles existem para um ser humano ler e para o seu log guardar. Nunca ramifique a sua lógica pelo texto de um erro — use o código HTTP e, quando existir, os campos estruturados doexplain(campo,bytes,limite,hash_original,mau…). - Campos novos nas respostas. Podem aparecer a qualquer momento, sempre
aditivos e, nas rotas de compatibilidade, sempre no namespace
pm_*. Seu desserializador precisa ignorar campos desconhecidos. - As rotas fora de
/api/v1. O que o painel usa para falar com o serviço é contrato interno e muda junto com o painel. Não automatize nada por ele: tudo que uma integração precisa está em/api/v1.
Também não são contrato: os detalhes de infraestrutura (quantas instâncias
atendem, o valor de X-Pm-Instance, o campo git da raiz), a ordem de campos
dentro de um objeto JSON, e o desempenho de uma chamada específica.
Como o serviço evolui sem quebrar você
Três regras internas sustentam as promessas acima, e elas valem a pena porque explicam por que você pode confiar:
- Migrações de banco só somam. Nenhuma migração já publicada é editada.
- Extensões nascem em
pm_*. O formato de compatibilidade é congelado; tudo que é nosso entra em namespace próprio, para nunca colidir com um campo que a sua integração já lê. - Nada é aceito em silêncio. Um campo que mudaria quem recebe, quando recebe ou o que a pessoa vê e que ainda não implementamos devolve 400 nomeado, em vez de ser ignorado. A lista está em Migrando de outro provedor.
Depreciação
Quando algum comportamento precisar sair, o caminho é o mesmo da rotação de chaves: janela de convivência primeiro, corte depois. A rotação de chave de API é o exemplo já implementado — a chave anterior segue válida por 24 horas, e a rotação nunca produz 401.
Saúde do serviço
curl https://api.pushmesh.io/health
Devolve status, versao, git, papel, lider, uptime_s, instancia,
estado do banco e do cache, modo_degradado, lideranca, pg_down_ha_s e
fila_mais_antiga_s.
Responde 200 enquanto o serviço serve — inclusive com o cache fora (o banco é a verdade; o cache é acelerador) e durante uma falha curta de banco. Só cai para 503 quando o banco está fora há mais de 60 segundos ou a instância está em drenagem.
Veja também
| Assunto | Página |
|---|---|
| a tabela de códigos e a política de retry | Autenticação e erros |
| todos os tetos publicados | Limites |
| a assinatura e a reentrega dos webhooks | Webhooks |
| o que é aceito tal e qual vindo de outro provedor | Migrando de outro provedor |