O protocolo
É isto que um feeder, um relay ou um cliente precisa falar para interoperar. A referência é o crate wire no repositório do Tally, o único código ali que codifica ou decodifica o protocolo, e todo binário o usa. Os inteiros são sempre big-endian.
Transporte
Cada salto é Noise_IK_25519_ChaChaPoly_BLAKE2s sobre TCP. Cada mensagem Noise é enviada precedida de um comprimento de dois bytes, no máximo 65.535 bytes com a tag, e carrega apenas registros inteiros.
A identidade é uma única chave Ed25519. A chave X25519 que o Noise autentica é derivada dela (sk_to_curve25519 da libsodium), e a primeira mensagem de handshake do iniciador carrega sua chave pública Ed25519 de 32 bytes, que o respondedor confere com a chave que o handshake provou. Uma chave identifica uma antena em todo lugar.
O preâmbulo
Antes do handshake, o iniciador envia 16 bytes em texto puro. Eles também são o prólogo do Noise, então um byte alterado faz o handshake falhar, e permitem que o próprio handshake mude depois e que um relay mantenha uma chave antiga e uma nova enquanto faz a rotação.
Versões
Em seguida, o iniciador envia Hello com a menor e a maior versão de protocolo que fala e seu papel. O respondedor responde Welcome com a versão que ambos vão falar daí em diante, ou Goodbye com um motivo e quanto tempo esperar. Do primeiro byte até o Welcome são no máximo 15 segundos, e cada envio depois disso tem prazo de 30. A versão de protocolo 1 é a única até agora.
Um relay atende a toda versão de protocolo que algum feeder já tenha usado, para sempre. Feeders instalados não se atualizam, e o resto do protocolo se apoia nesta regra.
Registros
Um registro é um byte de tipo, um comprimento de corpo de dois bytes e o corpo. Estes são os tipos que a versão de protocolo 1 conhece:
Regras de mudança
Os feeders nunca se atualizam, então estas são as regras que deixam o protocolo mudar sem eles:
- Um tipo desconhecido é ignorado, a menos que seu bit alto (0x80) o marque como crítico, o que encerra a sessão de forma limpa. Por isso, registros novos não são críticos, a menos que ignorá-los seja um erro.
- Um corpo pode carregar bytes depois dos campos que um leitor conhece, e eles são ignorados. Campos são adicionados ao final.
- Um leitor mantém um valor de enum que não conhece como o valor Other, nunca como um erro.
- Uma forma assinada mantém o mesmo prefixo em toda versão de formato e termina com a assinatura sobre tudo o que vem antes, então um relay consegue verificar, rotear e deduplicar um formato que de resto não sabe ler.
- O formato de uma observação é o próprio byte de tipo do BEAST para 1090 MHz. O UAT de 978 MHz usa valores novos e não precisa de registro novo, e os relays repassam formatos que não conhecem.
O lote assinado
Um lote é cerca de um segundo de recepções de um receptor. O formato 1 é organizado assim:
Formatos de observação: 1 é Mode A/C (2 bytes), 2 Mode-S curto (7), 3 Mode-S longo (14), D um downlink UAT de 978 MHz e U um uplink UAT de solo, cada um escrito como seu caractere ASCII.
A assinatura cobre o texto ASCII tally batch, um byte zero e todos os bytes do lote antes da assinatura. Os lotes deduplicados do próprio relay assinam tally relay batch em vez disso, para que um nunca se passe pelo outro.
A identidade de um lote é o hash BLAKE2s-256 de todos os seus bytes, assinatura incluída. Os relays deduplicam por ela, e o campo Anterior do lote seguinte a referencia.
Os primeiros 49 bytes (formato, chave, ID de boot e sequência) mantêm sua posição em todo formato futuro, e a assinatura sempre vem por último.
Ninguém nunca calibra os contadores antes do MLAT: o MLAT de um relay precisa do contador bruto para calcular.