Tema
Como assinar bundles OTA em React Native (code signing)
Atualizado em outubro de 2026.
Resposta curta: assinar um bundle OTA significa que a CLI assina o hash de cada arquivo do update com uma chave privada, e o app, que tem a chave pública gravada na build nativa, recusa qualquer arquivo cuja assinatura não confere. No hot-updater, a assinatura é RSA com SHA-256 e acontece no deploy. No Pipa no Ar, ela é gerenciada: criamos a chave do app, a chave privada nunca sai do nosso lado e você liga em quatro passos.
Por que assinar se o update já vai por HTTPS
O HTTPS protege o caminho entre o servidor e o aparelho. Ele não diz nada sobre quem gerou o arquivo nem se o arquivo foi trocado antes de chegar ao servidor de downloads. Um update OTA roda código no aparelho de todos os seus usuários, então vale fechar as outras portas:
- um arquivo alterado no armazenamento ou num cache no meio do caminho;
- um bundle publicado por fora do fluxo de deploy;
- um erro de configuração que aponte o app para um servidor errado.
Com a assinatura ligada, o app só aplica arquivos assinados pela chave do seu app. Qualquer outra coisa é descartada antes de ser carregada.
Como a assinatura funciona no hot-updater
- No deploy, a CLI calcula o SHA-256 de cada arquivo do bundle (o JavaScript e os assets) e do manifest que lista todos eles.
- Cada hash é assinado com a chave privada, no esquema RSA PKCS#1 v1.5 com SHA-256.
- As assinaturas vão no manifest. O hash do próprio manifest é publicado já assinado.
- No aparelho, o SDK confere cada arquivo baixado com a chave pública que está na build nativa: no
Info.plistdo iOS e nos metadados do app no Android.
A regra que o SDK segue é simples:
| Build nativa | Update sem assinatura | Update assinado |
|---|---|---|
| Sem chave pública | Aceita (confere só o hash) | Recusa |
| Com chave pública | Recusa | Confere a assinatura |
Por isso a ordem dos passos importa: depois que uma build com a chave pública chega às lojas, todo update para ela precisa sair assinado.
O problema real: onde fica a chave privada
Assinar é a parte fácil. Difícil é guardar a chave.
Hospedando o hot-updater por conta própria, você gera o par com npx hot-updater keys generate e guarda a chave privada no computador de quem publica ou nos segredos do CI. Isso traz dois riscos: a chave vazar, e qualquer um com ela assinar updates; ou a chave se perder, e ninguém conseguir mais publicar para as builds que já estão nas lojas até sair uma build nova.
O hot-updater tem um caminho melhor, a assinatura remota: a CLI manda só o hash de cada arquivo (32 bytes) para um serviço que guarda a chave e devolve a assinatura. A chave privada nunca chega na máquina de quem publica.
Assinatura gerenciada no Pipa no Ar
O Pipa no Ar usa essa assinatura remota. Ao ativar, criamos um par de chaves RSA de 3072 bits só para o app. A chave privada fica cifrada do nosso lado e não pode ser baixada. No deploy, a CLI envia só os hashes, autenticada pelo token de deploy, e recebe as assinaturas.
Como ligar
- Ative no painel. No app, abra Assinatura e clique em Ativar assinatura.
- Ligue no config. No
hot-updater.config.ts, passesigning: truepara opipanoar():tsimport { bare } from "@hot-updater/bare"; import { pipanoar } from "@pipanoar/hot-updater"; import { defineConfig } from "hot-updater"; export default defineConfig({ build: bare({ enableHermes: true }), ...pipanoar({ appId: "app_xxxxxxxxxxxxxxxxxxxx", signing: true }), updateStrategy: "appVersion", }); - Coloque a chave pública na build nativa.
- React Native bare: a CLI busca a chave e grava no
Info.pliste no Android:shnpx hot-updater keys export-public - Expo: baixe a chave pública na página Assinatura e aponte a opção
publicKeyPathdo plugin@hot-updater/expopara o arquivo.
- React Native bare: a CLI busca a chave e grava no
- Gere e publique uma build nativa nova. Daqui em diante, todo deploy sai assinado.
Com o signing: true no config, o comando de deploy não muda:
sh
npx hot-updater deploy -p ios -r 10O passo a passo oficial, com os avisos, está em Assinatura de bundles.
A ordem importa
Builds antigas, sem a chave pública, continuam aceitando updates sem assinatura. Builds com a chave só aceitam updates assinados. Então ligue o signing: true antes de publicar o primeiro update para a build nova. Se o app recusar o update depois de ligar a assinatura, quase sempre o deploy saiu sem ela: confira o config e publique de novo. Ver Dúvidas e problemas comuns.
O que a assinatura não resolve
Seja direto com o seu time sobre o que muda:
- O token de deploy continua sendo segredo. Na assinatura gerenciada, é ele que autoriza pedir assinaturas. Use um token com escopo Deploy, limitado a um app e a um pipeline, e revogue na hora se vazar. Ver Tokens e API keys.
- Código nativo não muda pelo ar, assinado ou não. Mudanças nativas continuam indo pela loja.
- Um bug assinado continua sendo um bug. Para isso existem o rollout progressivo e o rollback de um clique.
Trocar a chave
Troque a chave só se ela vazou. As builds que já estão nas lojas confiam só na chave antiga, então o roteiro é: trocar a chave na página Assinatura, gerar uma build nativa com a chave pública nova, publicar nas lojas e só então voltar a fazer deploy para essa build. O histórico de chaves fica na mesma página.
E se eu quiser sair do Pipa no Ar?
A assinatura usa o protocolo aberto do hot-updater. Para hospedar por conta própria, você gera a sua chave, coloca a pública numa build nova e publica com a sua infraestrutura. O roteiro completo está em Sem amarra.
Perguntas frequentes
Preciso de uma chave por plataforma? Não. A chave é do app, e o mesmo par vale para iOS e Android.
Posso ligar a assinatura num app que já está em produção? Pode, a partir de uma build nativa nova. Com o signing: true, os updates saem assinados, e uma build sem a chave pública recusa update assinado. Com o updateStrategy: "appVersion", a build nova costuma ter uma versão nativa nova, e os updates assinados vão só para ela.
A assinatura está em todos os planos? Sim, inclusive no Free. Veja os planos.