0030 — Credencial FCM fora do jar e exigida no boot¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-20 · Decidido em: 2026-09-18
Contexto¶
A credencial do firebase-admin vivia em src/main/resources/firebase-admin-sdk.json, versionada:
chave privada no git e, pelo empacotamento, também dentro do app.jar publicado no registry. A
property push.credentials-path já existia e o FcmPushService já lia de disco, mas o default era
classpath:firebase-admin-sdk.json — o arquivo embutido.
A migração para native (.ia/017) força a mudança: no native a credencial passa a depender de
metadata de recurso, e o jeito de injetá-la muda de qualquer forma. É a hora de tirá-la do build.
Havia ainda um problema de visibilidade. A credencial era aberta na primeira tentativa de push — credencial ausente ou path errado logava WARN só naquele momento. Numa instância que passa dias sem push, uma credencial trocada de lugar fica invisível justamente até a hora em que ela importa. Com a credencial saindo do jar para um secret file montado, "path errado" deixa de ser hipótese remota e passa a ser o modo de falha esperado de um deploy mal configurado.
Decisão¶
- A credencial sai do repo e do jar. Em produção ela chega por
PUSH_CREDENTIALS_PATHapontando para um secret file montado no container. Nenhuma mudança na assinatura doFcmPushService: a property já existia e o serviço já lia de disco. - Com o push ligado, a credencial é conferida no boot e a instância não sobe sem ela. Com
push.enabled=true, um@EventListenerdeStartupEventabre a credencial e lança se ela não abrir, com mensagem nomeando o path e a saída (PUSH_ENABLED=false). - Push desligado não exige credencial, e empresa não reconhecida (que já desliga o push) não derruba o boot.
Isso é o que a norma da casa manda: "property de segredo não declarada → feature desligada;
declarada e vazia → boot recusado, com a env nomeada" (engenharia/seguranca, Segredos).
PUSH_ENABLED=true é a feature declarada ligada. Não conflita com a
decisão 0026, que preteriu recusar o boot por
CENTRAL_API_TOKEN: lá a feature fica desligada nas instâncias que não a usam — o primeiro caso
da norma, não o segundo.
O gate é @EventListener(StartupEvent) e não @Requires(property=…, pattern=".+") porque a
condição não é "a property está vazia" e sim "a credencial abre": o path pode estar preenchido e
apontar para um arquivo que não existe, que é exatamente a falha que se quer pegar.
Alternativas preteridas¶
- Manter o aviso lazy (WARN no 1º push). Zero mudança, mas a troca para secret file é justo quando um path errado passa despercebido — e passaria dias, até o primeiro push.
- Avisar no boot com WARN, sem derrubar. Visível no deploy, mas um WARN em log de startup é lido por quem está olhando; o deploy seguiria verde com o push morto.
- JSON inteiro numa env (
PUSH_CREDENTIALS_JSON). Evita arquivo no disco, mas põe chave privada multiline na env do Coolify, visível emdocker inspect, e exigiria mudar oFcmPushService. - Manter no classpath com a chave rotacionada. Menor mudança de runtime, mas a chave continuaria viajando na imagem do registry — que é o problema de origem.
Consequências¶
- A ordem de deploy passa a importar: a env
PUSH_CREDENTIALS_PATHe o secret file precisam existir antes do deploy que remove o JSON do jar. Deployar antes derruba as instâncias comPUSH_ENABLED=true— hoje, as de push vivo. - Rotacionar a chave exposta no git é pré-requisito, e a rotação é o que invalida o vazamento; tirar do repo sozinho não invalida nada.
- O
resource-config.jsondo native continua incluindofirebase-admin-sdk.json: é inócuo quando a credencial vem de disco e ainda cobre o caminho de teste. - Config: application-config.