Saltar para o conteúdo principal

Webhooks de saída — L1

Implementado como escolha de design Aedifica. A gestão usa callables Firebase autenticadas, não endpoints REST /v1/webhooks. Só o proprietário e administradores da organização atual podem gerir destinos; a autoridade é revalidada na transação de escrita.

CallableEntradaResultado
createWebhook{url, events}Metadados e secret mostrado uma vez
rotateWebhook{id}Metadados e novo secret, anterior substituído
revokeWebhook{id}{revoked:true}, segredo eliminado
listWebhooks{}{items:[{id,url,events,active,createdAt}]}
listWebhookDeliveries{id}Últimas 50 entregas, sem payload ou segredo

O cliente nunca lê diretamente documentos que contêm credenciais. Os metadados seguros são projetados pelas callables. O segredo gerado tem 256 bits aleatórios e fica exclusivamente no servidor, em organizations/{orgId}/webhookSecrets/{id}. O limite é de 100 destinos ativos por organização, reservado transacionalmente em webhookLimits/active; revogar liberta uma vaga. A listagem inclui todos os ativos e até 100 revogados. A organização só pode ser derivada da identidade autenticada; não é aceite do corpo.

Eventos e corpo​

Recursos project, contact, document, catalog-item, time-entry, com sufixos .created, .updated, .deleted. Os triggers seguem as coleções canónicas projects, contacts, documents, catalogItems, times.

{"id":"identificador-estável-da-entrega","type":"project.updated","createdAt":"2026-11-04T12:00:00Z","data":{"id":"project-id","version":2}}

São notificações mínimas de referência: nomes, moradas, NIF, horas, valores e outros dados comerciais não são copiados para a fila nem para os logs. O consumidor autorizado pode consultar o recurso pela API. Para um recurso eliminado, a referência já não poderá ser consultada.

Assinatura e tentativas​

Cada POST leva Aedifica-Signature: t=<segundos Unix>,v1=<hex HMAC-SHA256>. Assinar os bytes UTF-8 de <timestamp>.<corpo JSON exato> com o segredo do destino. O consumidor deve verificar HMAC em tempo constante, exigir timestamp recente (por exemplo, cinco minutos) e deduplicar por id. Cada tentativa pode ter timestamp/assinatura novos.

webhookQueue/{id} mantém a fila durável e lease de cinco minutos. organizations/{orgId}/webhooks/{id}/deliveries/{deliveryId} guarda apenas tipo, identificador, estado, tentativas, código HTTP e datas. Repetições de um mesmo evento Firestore são deduplicadas por organização, webhook e ID do evento. A entrega é pelo menos uma vez: uma falha após o POST e antes da confirmação pode repetir o POST.

O scheduler retryWebhookDeliveries processa até 50 entregas devidas por minuto. Sucesso é HTTP 2xx; até oito tentativas, com recuo de 1, 2, 4, 8, 16, 32 e 60 minutos. Falhas finais e destinos revogados saem da fila; o histórico permanece. A revogação cancela entregas pendentes. Não são guardados corpos de resposta ou mensagens de erro dos destinos.

Proteção de destinos​

Apenas HTTPS na porta 443, sem credenciais URL nem fragmentos. Localhost e nomes locais são recusados. Os endereços DNS de cada tentativa têm de ser todos globais; IPv4 privados/reservados, IPv6 locais, endereços de transição e prefixos especiais são recusados. Um IP previamente validado fica fixado no socket, mantendo TLS/SNI no hostname original; não existe segunda resolução durante a ligação. Redirects não são seguidos. Timeout de dez segundos e resposta limitada a 64 KiB.

Operação​

As funções usam europe-west1, maxInstances:2, sem instâncias mínimas. O código não faz deploy nem altera IAM. É necessário ativar os triggers e Cloud Scheduler no deploy do orquestrador. Não são necessários segredos globais adicionais. A retenção/eliminação periódica de histórico de entregas é uma decisão operacional futura; não há expiração automática neste lote.