Apuração do 2º turno para presidente ainda não começou. No 1º turno, Flávio Bolsonaro teve 47,03% dos votos válidos e Lula, 45,16%. Dados do TSE às 09:38:12 de 15/10/2026.
Uma demonstração técnica
Este site é uma demonstração da EadTech, parceira da Adobe, montada no ambiente de testes (sandbox) que a Adobe oferece a parceiros. O objetivo é testar na prática o AEM Edge Functions, recurso que a Adobe lançou em beta, junto com o Edge Delivery Services, o Universal Editor e o App Builder. Não tem fins comerciais, não é um produto da Adobe e não é um serviço em produção.
O caso escolhido é exigente de propósito: um placar que muda a cada minuto, lido no celular, com projeção estatística e sem deixar a fonte dos dados exposta.
Quem faz o quê
São duas peças da Adobe trabalhando em turnos diferentes. A Edge Function roda na borda, a cada visita, e monta a página em poucos milissegundos perto de quem lê. O App Builder roda nos bastidores: uma ação acorda a cada minuto para ler o TSE e fazer as contas, outras duas respondem só à borda. Nenhuma visita chega ao TSE nem ao App Builder diretamente.
Passe o mouse (ou o foco do teclado) numa peça para ver com quem ela conversa.
Monta cada página: busca o HTML no Edge Delivery e o painel no App Builder ao mesmo tempo, coloca os números dentro da página e devolve tudo pronto, com os cabeçalhos de segurança.
Esconde os endereços de origem. Também responde /api/painel.json, /llms.txt e as rotas de consentimento.
O caminho de um acesso
Por que Edge Function
- A página já chega com o placar. Sem a borda, o navegador teria de abrir a página vazia e depois buscar os números. Com ela, o primeiro quadro já mostra o resultado, o que melhora a nota do Lighthouse e deixa o conteúdo legível para buscadores e agentes de IA.
- Um domínio só, nada exposto. O App Builder e o TSE ficam atrás da função. O segredo que destranca o App Builder entra pela CDN e nunca chega ao navegador nem ao código.
- Menos carga na origem. A borda responde com o painel guardado por 10 segundos e só renova uma vez. O App Builder recebe poucas chamadas por minuto, não importa quantas pessoas estejam lendo.
- Registro de consentimento no próprio domínio. A escolha de cookies é gravada pela borda, com recibo, e o relatório só abre com Adobe ID autorizado.
- Texto vivo para agentes. O /llms.txt sai da borda com a situação do momento.
As alternativas consideradas foram chamar o App Builder direto do navegador (expõe o endereço e abre a ação a qualquer um) e uma página estática com JavaScript buscando os dados (a página abre vazia e perde desempenho).
Por dentro, em detalhe
Esta parte é para quem vai abrir o código ou repetir a montagem em outro projeto. Os números de tempo foram medidos no domínio em 21/10.
1. O pedido, passo a passo
- Regras da CDN. O pedido chega à CDN gerenciada do AEM. Primeiro passam os filtros de tráfego: so-leitura bloqueia qualquer método que não seja GET, HEAD ou OPTIONS (a exceção é /consent-log), e painel-sem-enxurrada limita cada IP a 30 pedidos em 10 segundos nas rotas /api/* e /consent-*, com 60 segundos de castigo.
- O segredo entra no pedido. Uma request transformation põe o cabeçalho x-require-whisk-auth com o valor da variável secreta APURACAO_ORIGEM_SEGREDO do pipeline. O navegador nunca vê esse valor, e ele não está no código.
- Escolha da origem. Um origin selector manda para a origem edgefunction-apuracao-borda as páginas (caminho sem extensão), o /llms.txt, o /api/painel.json, o /api/serie.json e as três rotas de consentimento, estas sem cache. Scripts, estilos, fontes, imagens e planilhas vão direto ao Edge Delivery, com cache normal.
- A Edge Function busca as duas origens em paralelo. Com Promise.all, pede o HTML ao Edge Delivery (cache de 60 s na borda) e o painel ao App Builder (cache de 10 s), repassando o segredo. Medido sem cache: de 100 a 320 ms para as duas buscas, e 1 a 2 ms para montar a página.
- Montagem. A função injeta no <head> uma ilha <script type="application/json" id="painel-inicial"> com o painel, escapado para não fechar a tag nem abrir comentário HTML, e põe no começo do <main> um resumo em texto para quem não executa JavaScript (buscadores e agentes de IA). O JavaScript da página retira esse resumo na fase eager.
- Resposta. Sai com cache-control: public, max-age=0, s-maxage=10, stale-while-revalidate=30: a CDN guarda 10 s e pode servir a cópia anterior por mais 30 s enquanto renova. Os pedidos que chegam dentro desses segundos são atendidos com uma busca só à origem. O cabeçalho server-timing leva os dois tempos até o navegador, e a faixa do topo os mostra.
2. A Edge Function, por dentro
- JavaScript sobre o Fastly Compute (@fastly/js-compute), compilado para WebAssembly. Um arquivo de entrada, borda/src/index.js, e a montagem em montar.js, que é pura e tem teste no Node.
- A configuração vem da ConfigStore('config_default'), que é do programa inteiro. Por isso as chaves deste site levam o prefixo APURACAO_, e o código tem um valor padrão para cada uma, porque o sandbox não cria a config store nem cofre de segredos para Edge Function.
- Lista fechada de destinos: a função só faz fetch ao host do Edge Delivery e ao host do Runtime. Qualquer outro destino é recusado antes de sair. É o que faz o papel do campo origins, que o Cloud Manager não aceita em site Edge Delivery.
- Cache por busca com CacheOverride('override', { ttl }): 60 s para página, 300 s para o llms.txt, 10 s para o painel.
- O Edge Delivery às vezes entrega texto em gzip mesmo para quem não pediu compressão. A busca usa fastly: { decompressGzip: true } e, se ainda vier compactado, a função descompacta com DecompressionStream. Sem isso o /llms.txt respondia 502.
- Os cabeçalhos de segurança (HSTS com preload, nosniff, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP, CORP e COEP) saem da própria função. As response transformations do cdn.yaml não se aplicam ao que uma Edge Function devolve. A função também apaga os cabeçalhos que contariam o que há por trás (x-openwhisk-activation-id, x-powered-by, x-gw-cache) e o x-robots-tag: noindex que o aem.live põe nas próprias respostas.
- Atrás da CDN, o host público chega em x-forwarded-host, e não em host. É ele que a rota de consentimento usa para conferir de que site veio o registro.
- Qualquer exceção vira 502 com no-store e mensagem curta, ainda com os cabeçalhos de segurança, e o erro vai para o log da função.
- Implantação: o pipeline de configuração do Cloud Manager declara a função (edgeFunctions.yaml). Depois, aio aem edge-functions deploy envia o pacote com uma credencial OAuth Server-to-Server que tem a AEM CDN API e o escopo aem.cdn. A API de implantação mora no próprio domínio, em /adobe/..., e por isso a regra de só leitura abre exceção para esse caminho.
3. O App Builder, por dentro
- coletar: Node.js 22, 512 MB, limite de 58 s, sem exposição web. Um gatilho do pacote de alarmes do Runtime (/whisk.system/alarms/alarm) a dispara a cada minuto. Cada ciclo lê os 29 arquivos de total (26 estados, DF, exterior e Brasil), todos com If-None-Match: o que não mudou volta 304 e quase não custa nada ao TSE. Depois lê até 600 arquivos de município, só os que ainda não fecharam, com prazo de 40 s. Em erro 429 ou 5xx tenta de novo com espera crescente e, se os 429 se repetirem, reduz o número de municípios dos ciclos seguintes. No fim roda o modelo e grava o retrato.
- App Builder State: as chaves meta (ETag e resumo de cada arquivo), m-UF (municípios de cada estado), painel (o JSON que a página usa) e serie (até 600 pontos da noite, para o gráfico ao longo da apuração). Os consentimentos ficam em r-<recibo>, por um ano.
- painel: ação web com final e require-whisk-auth. Sem o segredo certo, o próprio Runtime recusa o pedido antes de executar o código. Responde com cache-control: private, no-store para que o gateway do Runtime não guarde cópia: uma cópia guardada sairia para quem chamasse o Runtime direto. Quem guarda é a borda.
- consentimento: também trancada pelo segredo. Três operações: registrar (cada escolha, com recibo), recibo (a pessoa confere a dela) e exportar (o relatório). Para exportar, a página /consentimentos faz login com Adobe ID pelo fluxo OAuth com PKCE para aplicação de página única do IMS, guarda o token só na sessão do navegador e o manda como Bearer. A borda repassa o token em x-adobe-token, e a ação confere três coisas: que o token foi emitido para o nosso client ID, que o IMS ainda o aceita (o perfil responde) e que o e-mail está na lista de autorizados. No log, o e-mail aparece só como prefixo de um hash SHA-256.
- Implantação pela API REST do Runtime (appbuilder/publicar.mjs), com o segredo e a lista de autorizados vindos do .env, que não vai para o repositório. Depois da apuração, node publicar.mjs --parar desliga o alarme.
4. O modelo da projeção
- A unidade é o município: são 5.757, exterior incluído. A virada de cada um é o logit da fatia de Flávio entre os dois no 2º turno menos o logit da fatia no 1º turno.
- Coeficientes de transferência (a fatia dos outros candidatos no 1º turno e a regressão à média) estimados dentro de cada UF, com ridge. Hierarquia país, região e UF, com prior nacional das pesquisas.
- Os municípios pesam votos × f⁴, em que f é a fração já apurada. A parcial de uma cidade grande engana, porque as zonas que fecham primeiro não são iguais às que fecham depois, e assim a virada vem sobretudo de quem já terminou.
- Incerteza por Monte Carlo com 3.000 simulações (região, UF, parte não apurada de cada UF, município e razão entre comparecimento e votos válidos). A chance só aparece com 15% das seções apuradas e urnas em pelo menos 20 UFs.
- Teste com o 2º turno de 2022, com as zonas entrando na ordem real em que o TSE as fechou: erro de cerca de 0,1 ponto desde 15% apurado e o intervalo de 90% contendo o resultado em todos os pontos medidos.
5. A página no navegador
- Edge Delivery Services com autoria no Universal Editor (modelo xwalk). Cada bloco tem o seu modelo de conteúdo; textos de interface vêm de uma planilha de rótulos, e as pesquisas, de cinco planilhas publicadas como JSON.
- Carga em três fases: na eager entra só o necessário para o maior elemento da tela, o número do placar; o resto vem na lazy e na delayed. A fonte dos números é pré-carregada, e as fontes de reserva têm métricas ajustadas para a troca não mexer no layout.
- O placar lê a ilha painel-inicial antes de ir à rede, então o primeiro quadro já tem os números e não há salto de layout. Depois confere o /api/painel.json a cada 15 s com a aba visível e a cada 60 s com ela escondida, e redesenha só o que mudou.
- Todas as áreas que mudam têm altura reservada. A faixa do topo tem altura fixa pelo mesmo motivo.
- CSP com nonce e strict-dynamic declarada no head.html; o Edge Delivery a move para o cabeçalho e troca o nonce a cada resposta.
- Publicar no Universal Editor dispara a invalidação por push na CDN gerenciada: o conteúdo novo aparece no domínio em cerca de 10 segundos. O código vai do GitHub para o Edge Delivery pelo Code Sync.
- Uma esteira do GitHub roda o Lighthouse no domínio publicado, três vezes por perfil (celular e computador), toda hora e de 15 em 15 minutos na noite da apuração, e grava o resultado que a faixa do topo lê.
6. O que o sandbox muda
- Sem cofre de segredos nem KV para Edge Function: o segredo entra pela CDN, como cabeçalho, e os registros moram no App Builder State.
- A configuração da CDN é uma só para todos os sites Edge Delivery do programa. As regras deste site ficam no mesmo arquivo dos outros, cada uma limitada pelo domínio, e saem com um git revert depois da apuração.
Como a projeção é calculada
Cada município é comparado com ele mesmo no 1º turno, só entre os dois candidatos. A diferença medida nas cidades já apuradas estima o que falta em cada estado, com uma hierarquia país, região e estado. Os municípios entram com peso maior quanto mais completa está a apuração deles, porque a parcial de uma cidade grande engana. A incerteza vem de 3.000 simulações. A chance de vitória só aparece a partir de 15% das seções apuradas e com urnas em pelo menos 20 estados.
No teste com o 2º turno de 2022, na ordem real de chegada das urnas, o erro médio da projeção foi de 0,04 ponto percentual.
Desempenho, acessibilidade e segurança
- Lighthouse medido pela esteira do GitHub no site publicado, no perfil de celular. A faixa do topo mostra a última medição e também o que o seu navegador mediu neste acesso.
- Nota A+ no securityheaders.com: CSP com nonce, HSTS, isolamento entre origens (COOP, COEP, CORP) e filtros de tráfego na CDN.
- Perfil de acessibilidade com contraste, tamanho de texto, redução de movimento e atalhos de teclado.
- Recursos novos dos navegadores testados com cuidado: View Transitions, Popover com CSS Anchor Positioning, container queries e WebMCP. Quem não tem o recurso vê os mesmos números, sem o efeito.
O que aprendemos com o Edge Functions
- Num programa com vários sites Edge Delivery, o Cloud Manager aplica uma configuração de CDN só para todos. As regras de cada site precisam ficar no mesmo arquivo, separadas pelo domínio.
- Os cabeçalhos definidos no cdn.yaml não chegam ao que a Edge Function devolve: a própria função precisa colocá-los.
- A API de deploy da função passa pelo domínio do site. Um filtro de tráfego que bloqueia escrita precisa deixar o caminho /adobe/ livre.
- O Edge Delivery entrega alguns textos já compactados. A função precisa descompactar antes de mexer no corpo.
- No sandbox não há config, segredos nem KV para a função: os valores ficam como padrão no código e o segredo entra pela CDN.
Aviso
Os números oficiais são os do Tribunal Superior Eleitoral. Este site lê os arquivos públicos do TSE e pode atrasar, falhar ou mostrar dados incompletos. A projeção e a chance de vitória são estimativas estatísticas, não resultado. O site não tem relação com o TSE, partidos, candidatos ou campanhas.
É uma demonstração técnica da EadTech no sandbox de parceiro da Adobe, usado conforme os termos do programa: para demonstração e capacitação, sem produção, sem piloto, sem teste de carga e sem guardar dado pessoal. O AEM Edge Functions é um recurso novo e partes do site podem deixar de funcionar a qualquer momento. Não é um produto nem um serviço da Adobe.
Fontes: resultados e fotos dos candidatos vêm de bases públicas do TSE (arquivos de resultado e DivulgaCandContas). As pesquisas citadas são as registradas no TSE.