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.

BytesCampoSignificado
0–3Número mágicoAs letras ASCII TALY.
4Versão do preâmbulo1.
5Handshake1: o padrão Noise acima.
6–7ReservadoZero.
8–15Dica de chavePara qual das chaves do respondedor a conexão é feita: os primeiros 8 bytes do BLAKE2s-256 da chave X25519 dele.

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:

TipoRegistroCorpo
0x01HelloMenor versão, maior versão (dois bytes cada), papel (um byte: 1 feeder, 2 assinante, 3 relay, 4 MLAT, um relay que publica posições).
0x02WelcomeA versão que ambos falam daqui em diante (dois bytes).
0x03BatchO lote assinado de um receptor, descrito abaixo. Os relays o repassam byte a byte.
0x04ReceiverInfoA descrição assinada que um receptor faz de si mesmo: localização e sua precisão, classe de relógio, software e os relays que escolheu.
0x05HeartbeatVazio. Sinal de vida.
0x06RedirectSegundos de espera (quatro bytes) e relays como sugestões. Significa apenas “me deixe”; o feeder continua escolhendo pelas próprias regras.
0x07RelayListRelays que este relay conhece, oferecidos como sugestões.
0x08RateLimitBytes por segundo e burst (quatro bytes cada): o máximo que o relay quer. Um feeder pode ficar abaixo, nunca acima do limite definido pelo dono.
0x09GoodbyeUm motivo (um byte) e segundos de espera (quatro bytes): o relay está encerrando.
0x0aSubscribeFluxos (um byte de flags: 1 bruto, 2 deduplicado, 4 posições), uma contagem (dois bytes) e essa quantidade de células H3 (oito bytes cada), no máximo 64. Substitui o Subscribe anterior do assinante.
0x0bRelayBatchUm lote que o próprio relay assinou: seu fluxo deduplicado.
0x0cPositionsPosições que o MLAT de um relay calculou, assinadas por esse relay.

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:

BytesCampoSignificado
1Formato1.
32ChaveA chave pública Ed25519 do receptor.
8ID de bootAleatório a cada vez que o feeder inicia.
8Sequência0, 1, 2 e assim por diante dentro de um mesmo boot. Uma lacuna mais adiante na cadeia são dados que algum salto não entregou.
4Época do contadorIncrementada sempre que o contador do receptor pode ter reiniciado, para que contadores de antes e de depois nunca se misturem.
8Hora do sistemaO relógio do feeder em milissegundos Unix. Informativo: pode estar errado.
4DescartadasRecepções que o próprio limite do feeder descartou desde o lote anterior.
32AnteriorO hash do lote anterior deste boot, zeros no primeiro, para que um receptor não possa assinar dois históricos.
4ContagemQuantas observações vêm a seguir.
10 + n cadaObservaçõesCada uma é um formato (um byte), o contador bruto de 48 bits do receptor (seis), o nível de sinal (um), um comprimento (dois) e essa quantidade de bytes da mensagem como o decodificador a emitiu.
64Assinatura Ed25519Sobre tudo o que vem antes.

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.