WaWeb canonical pipeline

Do socket à mensagem

Como o WhatsApp Web sai do nada e chega a mandar e receber uma mensagem: abre um canal seguro, pede autorização ao celular principal, autentica a sessão, baixa o estado da conta e troca mensagens.

Como ler este relatório

O fluxo tem três níveis ao mesmo tempo. O WebSocket carrega frames binários; alguns frames viram stanzas, que são as mensagens estruturadas do protocolo; o relatório usa a sequência do pipeline para preservar a ordem exata em que tudo aconteceu.

Pipeline seq

Número do evento na linha do tempo completa. Inclui transporte, frames, Noise e stanzas.

Direções

C → S é cliente para servidor. S → C é servidor para cliente.

Frame

Pacote binário bruto que passa pelo WebSocket. Antes da primeira stanza, ele é usado pelo Noise.

Stanza

Mensagem estruturada do WhatsApp. Tem direção, tag, atributos e filhos.

Noise vs. Signal

Noise protege o canal WebSocket. Signal protege o conteúdo das mensagens ponta a ponta.

Observado vs. inferido

Observado vem direto da captura. Inferido é interpretação do papel daquele evento no fluxo.

Cliente WaWebO aparelho Web que queremos reproduzir em C#.
ServidorO WhatsApp, que aceita sockets, autentica e entrega stanzas.
Celular principalA linha que autoriza o pareamento pelo PairCode.
Celular auxiliarO aparelho de teste que dispara a mensagem recebida no fim.
Captura canônicaO cliente oficial observado, usado como referência para o C#.
Ver linha narrativa e exemplo seguro
O navegador abre sockets quase em paralelo e usa os que carregam frames úteis. Noise negocia um canal cifrado antes de qualquer stanza aparecer. PairCode vincula o cliente como aparelho companion autorizado. A sessão reconecta, recebe success e inicia o bootstrap operacional. O cliente sincroniza chaves, dispositivos, perfil, privacidade e mensagens pendentes. A mensagem é enviada, replicada por fanout e uma mensagem externa é recebida.
Exemplo seguro de stanza fictícia
tag: message
direção: recv
attrs: [from, id, type]
children: [enc, reporting]
valores reais: ocultos

Mapa visual do fluxo

Socket Noise PairCode Auth Bootstrap Envio Recebimento
1 · Socket + Noise O navegador abre o canal e cria um túnel seguro com o servidor.
WebSocket criado ws-1 · HTTP upgrade · 101

O navegador chama o servidor do WhatsApp e mantém uma linha aberta para trocar dados.

Ver detalhes desta etapa
Primeiro cumprimento seguro (client_hello) frame sent · 43B

O navegador manda um primeiro cumprimento cifrado com uma chave descartável.

Ver detalhes desta etapa
Resposta segura (server_hello) frame recv · 350B

O servidor devolve sua parte para os dois lados fecharem o canal seguro.

Ver detalhes desta etapa
Canal seguro pronto (client_finish) frame sent · 372B

O navegador conclui a negociação. A partir daqui as stanzas trafegam cifradas.

Ver detalhes desta etapa
Primeiro WAP decodificado recv iq · pair-device

Primeiro nó WAP decodificado depois do canal seguro abrir.

Ver detalhes desta etapa
2 · PairCode O navegador prova ao celular principal que pode entrar como aparelho companion.
pair-device S → C · refs de pareamento

O servidor manda referências temporárias para combinar com o celular principal.

Ver detalhes desta etapa
companion reg C → S · stage + JID/chaves

O navegador se apresenta em etapas e envia suas chaves de identidade.

Ver detalhes desta etapa
country + ref S → C · country_code + ref

O servidor informa o país do número e devolve uma nova referência.

Ver detalhes desta etapa
primary hello S → C · notification; depois C → S · ack

O celular principal entra na conversa; o navegador confirma que recebeu.

Ver detalhes desta etapa
pair-success S → C · pair-success; depois C → S · pair-device-sign

O servidor autoriza o novo aparelho; o navegador assina como prova.

Ver detalhes desta etapa
3 · Auth A sessão recém-criada é apresentada e aceita pelo servidor.
Troca de canal stream:error · xmlstreamend

O servidor encerra o canal antigo. A sessão real entra no canal seguinte.

Ver detalhes desta etapa
Novo socket ws-4 · reconexão autenticada

O navegador abre nova conexão com os dados combinados no pareamento.

Ver detalhes desta etapa
Registro da sessão (add) C → S · sessão registrada

O navegador entrega ao servidor as credenciais da sessão recém-criada.

Ver detalhes desta etapa
Login aceito (success) S → C · lid, props, companion key

O servidor aceita a entrada e devolve identidade e regras de uso.

Ver detalhes desta etapa
4 · Bootstrap A conta baixa configurações, dispositivos e mensagens antigas, como um app abrindo pela primeira vez.
Capacidades e rotas media_conn · props · edge_routing

O navegador pergunta por onde mandar mídia, quais regras valem e onde estão os servidores.

Ver detalhes desta etapa
Registro e chaves registration · identity · skey

O navegador envia chaves descartáveis usadas para cifrar conversas futuras.

Ver detalhes desta etapa
Privacidade privacy · tos · notices

O servidor entrega termos, privacidade e avisos que fazem parte do estado da conta.

Ver detalhes desta etapa
Mensagens e dispositivos active · usync · offline · devices

O navegador descobre aparelhos vinculados e baixa mensagens que chegaram antes dele.

Ver detalhes desta etapa
Perfil e chaves picture · business_profile · tokens · key

O navegador puxa foto, perfil, tokens e chaves vigentes antes do envio.

Ver detalhes desta etapa
5 · Envio O cliente manda a mensagem; o attr device_fanout indica a provável cópia para outros aparelhos.
Mensagem principal stanza 165 · sent message · participants + device-identity

O navegador envia a primeira cópia cifrada da mensagem.

Ver detalhes desta etapa
Ack + receipt stanzas 169-176 · phash, recipient, receipt

Servidor e cliente trocam confirmações de processamento e recibos de estado.

Ver detalhes desta etapa
Preparação do fanout usync · key · query · sync

Antes de replicar, o navegador confere chaves e dispositivos atuais.

Ver detalhes desta etapa
Fanout stanza 188 · sent message · device_fanout · inferido

Observamos o attr device_fanout; a leitura é cópia para outros aparelhos.

Ver detalhes desta etapa
6 · Recebimento Outro celular manda mensagem para esta conta e o navegador recebe.
Auxiliar envia contexto externo · não é stanza

O celular auxiliar dispara a mensagem que aparecerá no WebSocket.

Ver detalhes desta etapa
Mensagem recebida stanza 200 · enc + url_text + url_number + reporting

Chega uma mensagem cifrada; o texto real só existiria depois de decifrar.

Ver detalhes desta etapa
Receipt stanza 201 · C → S

O navegador avisa ao servidor: recebeu.

Ver detalhes desta etapa
Sync pós-recebimento usync · identity · final list

Antes de encerrar a captura, o navegador confere identidades e contatos.

Ver detalhes desta etapa

Eventos de fundo pós-pareamento

A nova captura de observação confirma uma coisa importante: depois do success, o WhatsApp Web não fica parado esperando o usuário. Ele continua sincronizando histórico, estado da conta, fotos, perfil, dispositivos e confirmações. Isso faz parte do pipeline normal e precisa ser tratado como tráfego legítimo, não como ruído.

Pareamento novoLogo após entrar, o Web recebe pendências, mensagens cifradas e confirmações antes do primeiro envio manual.
Reconexão por 5 minCom a sessão já pronta, ainda chegaram notificações offline, perfil, foto, identidade, lista e várias confirmações curtas.
Observação passivaSem enviar mensagem, a sessão registrou 3 minutos de eventos de fundo e pares curtos de iq em frames binários.
Ping?Não vimos ping WebSocket nativo. A melhor leitura atual trata sent iq vazio com xmlns=w:p como ping de aplicação.

1 · Histórico pendente e eventos offline

O servidor sinaliza que há coisas acumuladas para entregar. Na captura fresh, isso aparece como recv ib / offline na stanza 54 e, logo depois, várias recv message cifradas com meta, verified_name e enc. Na captura resume, aparecem notificações com attr offline e uma mensagem pendente com enc.

  • Observado: tags, attrs e filhos indicam lote pendente, mensagens cifradas e notificações marcadas como offline.
  • Inferido: chamamos isso de histórico pendente porque o conteúdo é cifrado e a captura publica só a estrutura.

Leia junto com mensagens e dispositivos e histórico pendente.

2 · Sync de conta e app-state

Nem todo evento de fundo é mensagem. Boa parte é manutenção do estado local: sync, usync, business_profile, picture, identity, list, devices e unified_session. Em linguagem simples: o Web está montando a mesma visão de conta que o app do celular já tem.

  • Observado: consultas e respostas de perfil, foto, identidade, dispositivos e listas continuam após o ready, momento em que o cliente oficial declara que a sessão está pronta.
  • Inferido: agrupamos esses eventos como app-state, porque são sincronizações do estado do aplicativo, não uma conversa de usuário.

Leia junto com success, perfil e chaves e sync pós-recebimento.

3 · Acks e receipts de fundo

Quando o servidor entrega lote offline ou notificação, o cliente responde com confirmações curtas. Na janela de reconexão de 5 minutos, foram vistos 25 sent ack no bloco inicial: 24 em sequência e mais 1 logo depois de uma troca curta de iq/add. Isso não é envio de texto; é o cliente dizendo que processou stanzas.

  • Observado: ack e receipt aparecem mesmo sem ação manual de envio.
  • Inferido: eles se correlacionam com as stanzas recebidas, por isso entram como confirmação de processamento.

Leia junto com ack + receipt do envio e ack.

4 · Batimento tratado como ping de aplicação

A captura de observação passiva mostrou pares pequenos e repetidos: sent iq curto e recv iq curto, carregados em frames binários de opcode 2. A captura resume com atributos seguros publicados pela captura confirmou que o envio usa xmlns=w:p. O opcode 2 quer dizer frame binário; os opcodes 9 e 10 seriam ping/pong nativos do WebSocket. Os pares começam cerca de 39 segundos depois da janela passiva e repetem em intervalos aproximados de 15 a 32 segundos.

Em linguagem comum, xmlns é uma etiqueta de destino dentro da stanza: ela indica para qual subsistema do WhatsApp aquele iq vai. A etiqueta w:p é a evidência usada aqui para categorizar esse batimento como ping de aplicação, mesmo sem um filho chamado ping.

  • Observado: sent iq vazio com xmlns=w:p, seguido por recv iq vazio.
  • Não observado pelo recorder: opcode WebSocket 9/10 ou stanza chamada keepalive.
  • Conclusão: tratamos como ping de aplicação, transportado dentro do canal cifrado, não como ping nativo do WebSocket.

A cadência vem das janelas observadas de 3 e 5 minutos. Janelas mais longas, rede ruim ou inatividade prolongada podem mudar o intervalo.

Para o cliente C#, isso significa reproduzir o iq curto com xmlns=w:p e não depender de ping/pong WebSocket tradicional.

Relatório analítico por bloco

Esta seção abre cada caixa do mapa. Cada painel separa o que foi observado nos contratos de captura, o que é inferência de leitura e quais campos são apenas nomes estruturais. Nenhum payload, telefone, JID, cookie, token, chave, ref ou valor bruto da captura é publicado.

WebSocket criado

observadopipeline seq 4-14

Leitura

O navegador abre dois sockets quase em paralelo. O ws-1 carrega o pareamento; o ws-2 recebe 101 e fecha sem carregar stanza.

Entrada chromium_started, recorder anexado e handshake HTTP de WebSocket.
Saída ws-1 pronto para receber os frames binários do Noise.

Mini pipeline

SeqEventoContrato
4-5transportwebsocket_created para ws-1 e ws-2.
6-7requestHeaders por nome; valores como Cookie/User-Agent ficam fora do relatório.
8, 11responseHTTP 101 aceita o upgrade para WebSocket.
14closews-2 fecha sem stanza associada.

Ponto de convergência: o cliente C# deve reproduzir a existência de sockets auxiliares ou, no mínimo, explicar a divergência no diff.

client_hello

inferidopipeline seq 9-10

Leitura

O primeiro frame binário pré-stanza é interpretado pelo recorder como client_hello. Ele abre o handshake Noise e ainda não é uma stanza WAP.

Framews-1, direção sent, opcode binário, 43 bytes.
ConfiançaO evento vem de source=inferred_from_pre_stanza_binary_frame.

Mini pipeline

SeqCamadaContrato
9wirewebsocket_frame sent, payload_kind=binary, payload_size=43.
10noiseclient_hello, wire_frame_index=1.

Não publicar chave efêmera nem bytes do frame. O relatório só precisa da forma e do tamanho.

server_hello

inferidopipeline seq 12-13

Leitura

O servidor responde no mesmo ws-1 com um frame binário maior. A captura o classifica como a segunda etapa do Noise.

Framerecv, opcode binário, 350 bytes.
SaídaCliente tem material para finalizar o handshake.

Mini pipeline

SeqCamadaContrato
12wirewebsocket_frame recv, payload_size=350.
13noiseserver_hello, wire_frame_index=2.

client_finish

inferidopipeline seq 15-16

Leitura

O terceiro frame pré-stanza encerra a negociação Noise. Depois dele, o próximo evento estruturado já é uma stanza decodificada.

Framesent, opcode binário, 372 bytes.
TransiçãoNoise concluído → tráfego WAP decodificável.

Mini pipeline

SeqCamadaContrato
15wirewebsocket_frame sent, payload_size=372.
16noiseclient_finish, wire_frame_index=3.

Primeiro WAP decodificado

observadostanza 1 · seq 17

Leitura

Esse é o primeiro nó WAP legível depois do Noise. Ele prova que o canal cifrado já transporta stanzas estruturadas.

Stanzarecv iq, attrs [from,id,type,xmlns].
Filhopair-device, contendo 6 filhos ref com conteúdo em bytes.

Mini pipeline

StanzaSeqContrato
117recv iq / pair-device.
218sent iq / empty, resposta curta ao servidor.

pair-device

observadostanzas 1-2

Leitura

O servidor entrega referências temporárias para amarrar a tentativa de pareamento ao celular principal. Os valores das refs não devem sair da captura local.

Recebidopair-device com 6 ref.
Respondidosent iq vazio, usado como confirmação técnica.

Mini pipeline

StanzaSeqContrato
117recv iq, child pair-device, refs em bytes.
218sent iq, sem filhos.

companion reg

observadostanzas 3-4 e 9

Leitura

O cliente se apresenta como companion em etapas. Primeiro envia o estágio, depois manda plataforma, chaves públicas e nonce, e mais tarde envia o pacote de chave para o pareamento.

Campos voláteisjid, nonce, refs, chaves públicas e bundles.
Contrato fixoTag link_code_companion_reg, attrs por nome e ordem relativa dos estágios.

Mini pipeline

StanzaSeqContrato
321sent iq / link_code_companion_reg, attr stage.
423Attrs jid, should_show_push_notification, stage; filhos de chaves, plataforma e nonce.
933Attrs jid, stage; filhos link_code_pairing_wrapped_key_bundle, companion_identity_public, link_code_pairing_ref.

country + ref

observadostanzas 5-6

Leitura

São duas respostas distintas: uma informa o país do número e a outra devolve uma nova referência de pareamento. O mapa junta as duas por leitura visual, mas o contrato separa.

Stanza 5country_code com attr iso.
Stanza 6link_code_companion_reg com link_code_pairing_ref.

Mini pipeline

StanzaSeqContrato
525recv iq / country_code.
627recv iq / link_code_companion_reg, child link_code_pairing_ref.

primary hello

observadostanzas 7-8

Leitura

O servidor entrega a notificação vinda do aparelho principal. Ela carrega chave efêmera, identidade primária e ref. O cliente responde com ack.

Recebidonotification / link_code_companion_reg com attrs [from,id,t,type].
Respondidosent ack com attrs [class,id,to,type].

Mini pipeline

StanzaSeqContrato
729link_code_pairing_wrapped_primary_ephemeral_pub, primary_identity_pub, link_code_pairing_ref.
830sent ack da notificação.

pair-success

observadostanzas 10-12

Leitura

O servidor aceita o pareamento e entrega metadados de jurisdição, criptografia, plataforma, identidade do dispositivo e dados de negócio. O cliente fecha com pair-device-sign.

pair-successjurisdiction, encryption-metadata, client-props, platform, device-identity, biz, device.
Assinaturapair-device-sign / device-identity com key-index.

Mini pipeline

StanzaSeqContrato
1035recv iq vazio.
1137recv iq / pair-success.
1239sent iq / pair-device-sign.

Troca de canal

observadocausa inferidastanzas 13-14

Leitura

O fato observado é o fechamento do stream antigo com stream:error e xmlstreamend. A interpretação de que isso prepara a reconexão autenticada vem da vizinhança do fluxo.

Fatorecv stream:error com attr code; recv xmlstreamend sem attrs.
Depoisws-3/ws-4 surgem nas seq 44-45; ws-1 fecha na seq 48.

Mini pipeline

Stanza/SeqEventoContrato
13 / 41stanzarecv stream:error, sem publicar o valor do código.
14 / 42stanzarecv xmlstreamend.
44-45transportwebsocket_created ws-3/ws-4, antes do fechamento do ws-1.
48transportwebsocket_closed ws-1.

Novo socket

observadopipeline seq 44-53

Leitura

A reconexão também abre dois sockets quase em paralelo. O ws-4 carrega a sessão autenticada; o ws-3 aparece no handshake e fecha.

Socket útilws-4, handshake 101, frames binários e stanzas seguintes.
Socket auxiliarws-3, handshake 101, fechado na seq 53.

Mini pipeline

SeqEventoContrato
44-45createdws-3 e ws-4.
46-49,51handshakeRequests e responses 101.
50,52wireFrames 43B e 350B no ws-4.
53closews-3 fechado.

Nesta reconexão, a captura mostra dois frames pré-stanza no ws-4; não há evento client_finish separado como no primeiro socket.

add da sessão

observadostanza 15 · seq 54

Leitura

O cliente apresenta ao servidor o material de sessão recém-criado pelo pareamento. Esse é o primeiro envio estruturado no novo socket útil.

Stanzasent iq / add, attrs [id,to,type,xmlns].
Conteúdoadd com attr t e bytes redigidos.

Mini pipeline

SeqEventoContrato
54stanzasent iq / add.
55-56wireFrames enviados no ws-4; payloads 165B e 1545B.

success

observadostanza 16 · seq 57

Leitura

O servidor aceita a sessão. A partir daqui a captura entra no bootstrap operacional: mídia, props, privacidade, sync, identidade e mensagens pendentes.

Na captura de observação, esse ponto também marca o começo dos eventos de fundo pós-pareamento: a sessão continua viva e sincronizando mesmo sem envio manual.

Attrsabprops, companion_enc_static, creation, lid, location, props, t.
PublicaçãoSomente nomes dos atributos; valores são material sensível.

Mini pipeline

StanzaSeqContrato
1657recv success, conteúdo vazio, attrs de identidade e propriedades.
17-1959-61Primeiras consultas de bootstrap: media_conn, props, iq vazio.

Capacidades, mídia e rotas

observadostanzas 17-28

Leitura

O cliente busca conectividade de mídia, propriedades de protocolo, rotas e estado sujo. O estado offline aparece depois, na stanza 54, já no bloco de mensagens e dispositivos.

Obrigatórios observadosmedia_conn, props, edge_routing, dirty.
ComplementaresConsultas de foto, privacidade e respostas vazias.

Mini pipeline

StanzaSeqContrato
17-1959-61sent iq: media_conn, props, vazio.
20-2165-67recv ib: edge_routing, dirty.
22-2868-81picture/privacy e resposta media_conn.

Registro Signal e chaves

observadostanzas 29-33

Leitura

É um dos maiores pontos estruturais do bootstrap. O cliente envia registro, identidade, uma lista grande de prekeys e skey. A captura também recebe erros nessa vizinhança.

Stanza 29registration, type, identity, list com 812 key, skey.
Risco de diffNão comparar bytes de chaves; comparar presença, contagem e formato.

Mini pipeline

StanzaSeqContrato
2983sent iq / registration+type+identity+list+skey.
3085recv iq / props+erid, com 1354 prop.
31,3387,91recv iq / error; observado, não tratado como falha automática.
3288sent iq / clean.

Privacidade, termos e avisos

observadostanzas 34-45, 71-84

Leitura

Essa parte não é apenas configuração visual. Ela sincroniza regras de privacidade, termos e avisos legais que precisam aparecer no contrato do cliente.

Contagens globaissent iq / privacy aparece 6 vezes na captura; uma delas é a stanza 24, antes deste painel.
AvisosStanza 84 contém 28 filhos notice.

Mini pipeline

StanzaSeqContrato
34-4593-115Ciclo inicial de privacy/list/respostas vazias.
71-74167-170Privacy, request e disclosures.
75-82175-189Sync, mensagem de fundo, disclosure, ack/receipt e novo privacy intercalados.
83-84191-193tos e lista de notice.

Mensagens pendentes, dispositivos e sync

observadostanzas 46-70, 75-123

Leitura

Esta parte mistura ações do cliente com eventos de fundo que chegaram enquanto a sessão carregava: presença ativa, consultas usync, mensagens cifradas pendentes, receipts, devices e snapshots de app-state.

Agora ela também serve como ponte para a seção de histórico pendente, porque a janela passiva confirmou que offline, notificações e mensagens cifradas podem chegar sem ação do usuário.

Mensagens de fundorecv message / meta+verified_name+enc: stanzas 62, 67, 76, 87, 95 e 104 neste bloco.
Multi-devicedevices, add, remove, identity e usync.
Eventos raroswa_ad_account_nonce, parameters, collection e devices aparecem aqui e ajudam o diff.

Mini pipeline

StanzaSeqContrato
46-53116-131active, usync, identity.
54-70133-165offline, devices, mensagens recebidas, sync, receipts.
75-123175-271Snapshots, notificações, disclosures, usync e patches; stanzas 71-74 ficam detalhadas em privacidade.

Essas mensagens não são a mensagem auxiliar final. São eventos recebidos durante o bootstrap e precisam ser tolerados no diff.

Perfil, presença e chaves finais do bootstrap

observadostanzas 124-164

Leitura

O fim do bootstrap consulta presença, participação, fotos, perfil de negócio, grupos, tokens e chaves. Ele termina imediatamente antes do primeiro envio de mensagem.

Eventos únicosunified_session, recv iq / groups, recv notification / remove.
Perfilpicture, business_profile, tokens, key, list; inclui a stanza 138 como mensagem de fundo tardia.

Mini pipeline

StanzaSeqContrato
124-132273-289presence, participating, query, picture, unified_session.
133-155290-335business_profile, fotos, groups, tokens, key.
156-164337-353Notificações, acks, sync e lista final pré-envio.

Mensagem principal

observadorótulo inferidostanza 165 · seq 355

Leitura

Esse é o primeiro sent message do cliente no baseline. Chamamos de mensagem principal porque ela aparece antes do envio marcado com device_fanout; o rótulo é leitura do fluxo.

Attrsid, peer_recipient_pn, to, type.
Filhosparticipants com dois to; device-identity em bytes redigidos.

Mini pipeline

StanzaSeqContrato
165355sent message / participants+device-identity.
166-168357-360Resposta relacionada a tokens de negócio e ack de notificação.

peer_recipient_pn é nome estrutural; o telefone real não deve aparecer no HTML público.

Ack + receipt do envio

observadostanzas 169-176

Leitura

Confirmações não são uma coisa só. ack confirma processamento de stanza; receipt comunica estado de mensagem; e ainda há uma troca curta de iq/add no meio do bloco.

A captura passiva mostrou que acks e receipts de fundo também aparecem fora do envio de texto, normalmente em resposta a lote offline ou notificação.

AckStanza 169 tem phash; stanzas 171 e 175 são acks enviados pelo cliente, com attrs diferentes.
ReceiptStanzas 170, 174 e 176 comunicam estado/recibo.

Mini pipeline

StanzaSeqContrato
169363recv ack, attrs incluem phash.
170365recv receipt, attrs incluem recipient.
171367sent ack com attrs [class,id,to,type].
172-173369-371sent iq / add e resposta recv iq vazia.
174-176373-377recv receipt, sent ack sem type, e sent receipt.

Preparação do fanout

observadostanzas 177-187

Leitura

Entre o envio primário e o device_fanout, o cliente consulta estado de usuários, chaves, query, sync e addons. Essa etapa evita tratar o fanout como uma repetição cega.

Consultasusync, key, query, sync, my_addons.
Respostasusync, result, sync, list, my_addons.

Mini pipeline

StanzaSeqContrato
177-179379-383sent iq / usync, recv ack e resposta recv iq / usync.
180-184385-392key, query, sync, result, my_addons.
185-187395-399sync e list finais antes do fanout.

Fanout

observadosemântica inferidastanzas 188-199

Leitura

O fato observado é um segundo sent message com attr device_fanout. A leitura de que ele replica a mensagem para dispositivos vem do nome do atributo e do contrato multi-device.

Stanza 188sent message, attrs incluem device_fanout, filhos participants e device-identity.
Depoismy_addons, sync, ack, receipt, tokens e acks finais.

Mini pipeline

StanzaSeqContrato
188401sent message / device_fanout.
189-190403-405recv iq / my_addons e recv iq / sync.
191-196407-417ack, receipt, sent ack, add, receipt, sent ack.
197419recv iq vazio.
198-199421-422notification / tokens e respectivo ack.

Dispositivo auxiliar envia

evento externoantes da stanza 200

Leitura

Este bloco é contexto do cenário de teste, não um evento do contrato de pipeline. A primeira evidência observada no protocolo é a mensagem recebida na stanza 200.

Não vem do JSONLAção do Android auxiliar.
Primeiro efeitorecv message / enc+url_text+url_number+reporting.

Mini pipeline

MarcoOrigemContrato
externocenárioDispositivo auxiliar de teste envia a mensagem.
200 / 425pipelinePrimeiro efeito observável no WebSocket.

Mensagem recebida

observadostanza 200 · seq 425

Leitura

A conta em teste recebe uma stanza message cifrada. Ela traz o envelope enc e filhos estruturais como url_text, url_number e reporting. O conteúdo real não é publicado.

Attrsfrom, id, notify, sender_pn, sts, t, type.
Filhosenc, url_text, url_number, reporting.

Mini pipeline

StanzaSeqContrato
200425recv message, conteúdo em filhos, enc em bytes.
201427sent receipt confirma recebimento.

A presença de url_text e url_number é estruturalmente relevante; valores ficam redigidos.

sender_pn é apenas o nome do atributo observado; o número real não é publicado.

Receipt de recebimento

observadostanza 201 · seq 427

Leitura

O cliente confirma para o servidor que recebeu a mensagem. Esse recibo é curto e não carrega o conteúdo da mensagem.

Stanzasent receipt, attrs [id,to].
DepoisO cliente continua com consultas de sincronização e identidade.

Mini pipeline

StanzaSeqContrato
201427sent receipt, sem filhos.
202-203429-431sent iq vazio e resposta recv iq vazia.

Sync pós-recebimento

observadorótulo inferidostanzas 202-211

Leitura

O protocolo não emite um “fim” explícito aqui. Chamamos de sync pós-recebimento a sequência final de iq, usync, identity, receipt, ack e list antes do encerramento do navegador.

Esse comportamento conversa com o sync de app-state: mesmo depois de uma mensagem recebida, a sessão ainda ajusta identidade, lista e estado local.

IQ curtoStanzas 202-203: ida e volta vazia antes do usync final.
UsyncStanzas 204-207: duas consultas e duas respostas.
Identity/listStanzas 208 e 211 trabalham identidade e lista com 203 usuários estruturais.

Mini pipeline

StanzaSeqContrato
202-203429-431sent iq vazio e resposta recv iq vazia.
204-207433-439usync enviado/recebido.
208441sent iq / identity, 203 filhos user.
209-211443-447receipt, ack, recv iq / list.
-449chromium_closed encerra a captura.

Erros observados que apareceram no caminho

observadopainel transversal

Leitura

A captura tem erros estruturais esperados ou tolerados. Eles não devem sumir do relatório, porque são pontos importantes para diff entre o cliente oficial e o cliente C#.

Streamstream:error seguido de xmlstreamend na troca de canal.
IQQuatro recv iq com child error durante bootstrap.

Mini pipeline

StanzaSeqContrato
1341recv stream:error, valor do code não publicado.
3187recv iq com child error; attrs do child [code,text].
3391recv iq com child error; attrs do child [code,text].
142-143309-311recv iq com child error; attrs do child [code,text].

Na validação de convergência, a presença e a posição desses erros importam mais do que os valores.

Depois de tudo isso, o que sabemos?

A ordem está cobertaO relatório preserva a linha do tempo desde o primeiro socket até o fechamento da captura.
O vazio foi preenchidoO espaço entre abrir o WebSocket e receber pair-device agora mostra o handshake Noise e a primeira stanza.
O fundo também importaMesmo sem ação do usuário, a sessão recebe offline, sync, acks e pares curtos de iq.
O cliente C# tem critérioO que precisa convergir é estrutura: ordem, tags, attrs, filhos e contagens. Valores brutos continuam privados.

Glossário dos termos do mapa

Tradução curta dos nomes mais técnicos que aparecem no fluxo. Os termos ficam no idioma do protocolo para facilitar o diff com os logs, mas a explicação mostra o papel de cada um.

WebSocket
Canal que fica aberto entre cliente e servidor, permitindo troca de dados nos dois sentidos sem abrir uma conexão nova a cada mensagem.
HTTP 101
Resposta do servidor que aceita trocar o HTTP comum por WebSocket. É o sim oficial para abrir o canal persistente.
frame
Pacote bruto que viaja pelo WebSocket. Antes de virar uma stanza legível, tudo aparece como frames binários.
Noise
Protocolo que cria o canal seguro. Depois do handshake Noise, o tráfego passa cifrado entre cliente e servidor.
client_hello
Primeira mensagem do handshake Noise. O cliente envia uma chave temporária para começar a negociação segura.
server_hello
Resposta do servidor no handshake Noise. Ela devolve dados temporários para o cliente continuar a negociação.
client_finish
Mensagem final do handshake Noise. O cliente fecha a negociação e deixa o canal pronto para tráfego cifrado.
stanza
Mensagem estruturada do protocolo, parecida com uma árvore: tem tag, atributos, conteúdo e filhos.
WAP
Formato binário usado pelo WhatsApp para transformar stanzas em bytes compactos antes de enviar pelo socket.
iq
Tipo de stanza usado para pedidos que esperam resposta, como consultar dados, configurar estado ou registrar algo.
PairCode
Código curto digitado no celular para autorizar o novo aparelho sem usar QR code.
pair-device
Pedido inicial do servidor para começar o pareamento. Ele carrega referências usadas pelo celular e pelo cliente.
companion
Nome do protocolo para o aparelho secundário, ou seja, o cliente que será vinculado ao celular principal.
ref
Identificador temporário do pareamento. Ele amarra a tentativa do companion com o celular principal.
primary hello
Mensagem do celular principal para o companion durante o pareamento. Ela confirma o pedido e envia chaves.
ack
Confirmação curta de recebimento. Diz que uma stanza chegou, sem carregar uma resposta de negócio.
pair-success
Stanza que marca o pareamento como concluído. Depois dela, o companion já pode operar como autorizado.
stream:error
Erro de stream usado aqui como fechamento controlado. O servidor encerra o canal antigo para forçar reconexão.
success
Confirmação de login depois da reconexão. A partir dela, a sessão foi aceita e pode sincronizar dados.
lid
Identificador interno da conta no modelo multi-device. Em muitos pontos ele substitui o telefone.
props
Conjunto de configurações e flags que o servidor entrega para ajustar o comportamento da sessão.
edge_routing
Informação de rota que ajuda o cliente a escolher por onde trafegar com os servidores do WhatsApp.
dirty
Sinal de que algum conjunto de dados precisa ser limpo, confirmado ou sincronizado de novo.
Signal
Protocolo de criptografia ponta a ponta. Ele protege o conteúdo para que só remetente e destino leiam.
skey
Bloco de chave assinada usado no material de criptografia do dispositivo. O relatório mostra só a presença, nunca o valor.
usync
Consulta de sincronização de usuários. Serve para descobrir dispositivos, identidades, status e chaves.
offline
Lote de mensagens e eventos que chegaram enquanto o cliente estava fora e precisam ser entregues ao reconectar.
histórico pendente
Nome didático para mensagens e eventos que ficaram acumulados enquanto o Web não estava conectado. A captura mostra a estrutura desses pacotes, mas não publica o conteúdo cifrado.
app-state
Estado local do aplicativo: perfil, foto, dispositivos, identidade, listas, privacidade e outros dados que precisam ficar iguais entre celular, Web e servidor.
unified_session
Marcador de sessão enviado durante o bootstrap para alinhar estado entre a sessão Web e o servidor.
device-identity
Bloco assinado que prova quais dispositivos pertencem a uma conta e ajuda a validar o envio cifrado.
receipt
Recibo de estado da mensagem. Pode indicar entrega, recebimento, leitura ou outro avanço do fluxo.
fanout
Envio da mesma mensagem para vários dispositivos do destinatário, cada um com seu envelope cifrado.
phash
Hash estrutural que aparece em confirmação de envio. Ajuda a correlacionar o ack sem expor o conteúdo da mensagem.
my_addons
Consulta de recursos adicionais da conta. No fluxo, aparece como checagem auxiliar antes de concluir o fanout.
enc
Filho da stanza que carrega o conteúdo cifrado pelo Signal. O texto real só aparece depois de decifrar.
pipeline
Linha do tempo completa da captura. Inclui eventos de transporte, frames, handshake, stanzas e fechamento.
seq
Número sequencial de um evento no pipeline. Ele permite dizer exatamente o que veio antes e depois.
sent
Direção cliente para servidor. No mapa aparece como C → S.
recv
Direção servidor para cliente. No mapa aparece como S → C.
attrs
Atributos de uma stanza, como nomes de campos. O relatório publica nomes, não valores sensíveis.
children
Filhos de uma stanza. Eles formam a árvore interna, por exemplo message contendo enc.
payload
Conteúdo carregado por um frame ou stanza. Pode ser bytes cifrados, árvore WAP ou dados redigidos.
opcode
Tipo técnico do frame WebSocket. Aqui o importante é saber quando ele carrega bytes binários.
ping
Checagem curta de conexão. Nos frames capturados pelo recorder, não apareceu ping nativo do WebSocket; tratamos o sent iq vazio com xmlns=w:p como ping de aplicação.
keepalive
Função de manter a conexão viva. Nos frames capturados, o keepalive visível é interpretado como ping de aplicação do WhatsApp, não como opcode WebSocket de ping/pong.
batimento de aplicação
Forma didática de falar dos pares curtos e repetidos de iq vistos na janela passiva. Como o envio usa xmlns=w:p, o relatório também chama isso de ping de aplicação.
redigido
Valor ocultado de propósito. A estrutura fica visível, mas telefone, token, chave, JID e payload bruto ficam fora.
captura canônica
Execução do WhatsApp Web oficial observada em detalhe. Ela vira a referência para comparar o cliente C#.
baseline
Registro de referência usado no diff. Se o C# divergir dele, a diferença aponta onde investigar.
fresh
Execução começando sem sessão salva. Por isso inclui pareamento novo, autenticação e primeiro bootstrap.
observe
Execução de observação passiva. Ela usa uma sessão já pareada, não envia mensagem e fica olhando o que o WhatsApp Web faz sozinho depois do ready, quando o cliente oficial declara que está pronto.
contrato
Forma esperada do evento: ordem, tag, attrs, filhos, direção e contagens. Valores variáveis não entram no contrato.
JSONL
Arquivo de dados com um JSON por linha. Foi usado como insumo local, mas não é publicado neste HTML.
cliente oficial
O WhatsApp Web real rodando no navegador. Ele é a fonte de verdade do projeto.
cliente C#
A implementação WaWeb que deve reproduzir a mesma estrutura do fluxo observado no cliente oficial.
celular principal
Aparelho dono da conta WhatsApp Business. Ele autoriza o PairCode e mantém a identidade principal.
celular auxiliar
Aparelho usado no teste para enviar a mensagem que aparece como recebida no fim da captura.

Marcos exatos da captura

Faixa Marco Contrato observado
Pipeline 9-16 Handshake Noise antes da primeira stanza client_hello → server_hello → client_finish
Stanza 1 Primeiro nó WAP decodificado recv iq / pair-device
Stanza 11 Pareamento aceito recv iq / pair-success
Stanza 16 Sessão autenticada recv success / lid + props + companion_enc_static
Stanza 54 / pipeline 133 Primeiro marco de histórico pendente recv ib / offline, seguido por mensagens cifradas e receipts no bootstrap
Observação seq 53-134 Janela passiva pós-ready 3 minutos sem envio manual: 82 eventos, 40 stanzas e sync de perfil, foto, conta e iq curtos
Observação +35s a +154s Ping de aplicação inferido sent iq vazio com xmlns=w:p + recv iq vazio em opcode binário 2; não houve opcode 9/10
Stanza 165 Primeiro envio de texto sent message / participants + device-identity
Stanza 188 Mensagem com device_fanout observado sent message / device_fanout + device-identity
Stanza 200 Mensagem recebida do auxiliar recv message / enc + url_text + url_number + reporting