El protocolo

Esto es lo que debe hablar un feeder, un relay o un cliente para interoperar. La referencia es el crate wire del repositorio de Tally, el único código del repositorio que codifica o decodifica el protocolo, y todos los binarios lo usan. Todos los enteros son big-endian.

Transporte

Cada salto es Noise_IK_25519_ChaChaPoly_BLAKE2s sobre TCP. Cada mensaje Noise se envía precedido de una longitud de dos bytes, con un máximo de 65 535 bytes incluida su etiqueta, y solo transporta registros completos.

La identidad es una única clave Ed25519. La clave X25519 que autentica Noise se deriva de ella (sk_to_curve25519 de libsodium), y el primer mensaje del handshake del iniciador lleva su clave pública Ed25519 de 32 bytes, que el respondedor comprueba contra la clave que demostró el handshake. Una sola clave identifica una antena en todas partes.

El preámbulo

Antes del handshake, el iniciador envía 16 bytes en claro. Son también el prólogo de Noise, así que un byte alterado hace fallar el handshake, y permiten que el propio handshake cambie en el futuro y que un relay mantenga una clave antigua y una nueva mientras la rota.

BytesCampoSignificado
0–3Número mágicoLas letras ASCII TALY.
4Versión del preámbulo1.
5Handshake1: el patrón de Noise anterior.
6–7ReservadoCero.
8–15Pista de claveA cuál de las claves del respondedor va dirigida la conexión: los primeros 8 bytes del BLAKE2s-256 de su clave X25519.

Versiones

Después, el iniciador envía Hello con las versiones de protocolo mínima y máxima que habla y su rol. El respondedor contesta con Welcome y la versión que ambos hablarán a partir de entonces, o con Goodbye, un motivo y cuánto esperar. Desde el primer byte hasta Welcome pasan como máximo 15 segundos, y cada envío posterior tiene un plazo de 30. Por ahora, la versión de protocolo 1 es la única.

Un relay responde a todas las versiones de protocolo que haya incluido alguna vez un feeder, para siempre. Los feeders instalados no se actualizan, y el resto del protocolo se apoya en esta regla.

Registros

Un registro es un byte de tipo, una longitud de cuerpo de dos bytes y el cuerpo. Estos son los tipos que conoce la versión de protocolo 1:

TipoRegistroCuerpo
0x01HelloVersión mínima, versión máxima (dos bytes cada una), rol (un byte: 1 feeder, 2 suscriptor, 3 relay, 4 MLAT, un relay que publica posiciones).
0x02WelcomeLa versión que ambos hablan a partir de aquí (dos bytes).
0x03BatchEl lote firmado de un receptor, descrito más abajo. Los relays lo reenvían byte a byte.
0x04ReceiverInfoLa descripción firmada que un receptor hace de sí mismo: ubicación y su precisión, clase de reloj, software y los relays que eligió.
0x05HeartbeatVacío. Indica que sigue vivo.
0x06RedirectSegundos de espera (cuatro bytes) y relays como sugerencias. Solo significa “déjame”; el feeder sigue eligiendo según sus propias reglas.
0x07RelayListRelays que conoce este relay, ofrecidos como sugerencias.
0x08RateLimitBytes por segundo y ráfaga (cuatro bytes cada uno): lo máximo que quiere el relay. Un feeder puede quedarse por debajo, nunca por encima del límite que fijó su dueño.
0x09GoodbyeUn motivo (un byte) y segundos de espera (cuatro bytes): el relay está cerrando.
0x0aSubscribeFlujos (un byte de indicadores: 1 en bruto, 2 deduplicado, 4 posiciones), una cantidad (dos bytes) y ese número de celdas H3 (ocho bytes cada una), 64 como máximo. Sustituye al Subscribe anterior del suscriptor.
0x0bRelayBatchUn lote que firmó el propio relay: su flujo deduplicado.
0x0cPositionsPosiciones que resolvió la MLAT de un relay, firmadas por ese relay.

Reglas para el cambio

Los feeders nunca se actualizan, así que estas son las reglas que permiten que el protocolo cambie sin ellos:

  • Un tipo desconocido se ignora, salvo que su bit alto (0x80) lo marque como crítico, lo que cierra la sesión de forma ordenada. Por tanto, los registros nuevos no son críticos salvo que ignorarlos fuera un error.
  • Un cuerpo puede llevar bytes después de los campos que conoce un lector, y estos se ignoran. Los campos se añaden al final.
  • Un lector conserva un valor de enum que no conoce como el valor Other, nunca como un error.
  • Una forma firmada mantiene el mismo prefijo en todas las versiones de formato y termina con su firma sobre todo lo anterior, así que un relay puede verificar, enrutar y deduplicar un formato que, por lo demás, no sabe leer.
  • El formato de una observación es el propio byte de tipo de BEAST para 1090 MHz. UAT de 978 MHz usa valores nuevos y no necesita un registro nuevo, y los relays reenvían los formatos que no conocen.

El lote firmado

Un lote contiene alrededor de un segundo de recepciones de un receptor. El formato 1 se organiza así:

BytesCampoSignificado
1Formato1.
32ClaveLa clave pública Ed25519 del receptor.
8Id de arranqueAleatorio cada vez que arranca el feeder.
8Secuencia0, 1, 2 y así sucesivamente dentro de un mismo arranque. Un hueco más adelante en la cadena son datos que algún salto no entregó.
4Época del contadorSe incrementa cada vez que el contador del receptor puede haberse reiniciado, para que los contadores de antes y de después nunca se mezclen.
8Hora del sistemaEl reloj del feeder en milisegundos Unix. Es informativo: puede ser incorrecto.
4DescartadasRecepciones que el propio límite del feeder descartó desde el lote anterior.
32AnteriorEl hash del lote anterior de este arranque, ceros para el primero, para que un receptor no pueda firmar dos historiales.
4CantidadCuántas observaciones siguen.
10 + n cada unaObservacionesCada una es un formato (un byte), el contador en bruto de 48 bits del receptor (seis), el nivel de señal (uno), una longitud (dos) y ese número de bytes del mensaje tal como lo emitió el decodificador.
64Firma Ed25519Sobre todo lo anterior.

Formatos de observación: 1 es Mode A/C (2 bytes), 2 Mode-S corto (7), 3 Mode-S largo (14), D un downlink UAT de 978 MHz y U un uplink UAT de tierra, cada uno escrito como su carácter ASCII.

La firma cubre el texto ASCII tally batch, un byte cero y todos los bytes del lote anteriores a la firma. Los lotes deduplicados propios de un relay firman en su lugar tally relay batch, así que uno nunca puede hacerse pasar por el otro.

La identidad de un lote es el hash BLAKE2s-256 de todos sus bytes, firma incluida. Los relays deduplican con él, y el campo Anterior del siguiente lote lo nombra.

Los primeros 49 bytes (formato, clave, id de arranque y secuencia) mantienen su posición en todos los formatos futuros, y la firma siempre va al final.

Nadie calibra nunca los contadores antes de que lleguen a la MLAT: la MLAT de un relay necesita el contador en bruto para resolver.