Il protocollo

Ecco cosa deve parlare un feeder, un relay o un client per interoperare. Il riferimento è il crate wire nel repository di Tally, l’unico codice del repository che codifica o decodifica il protocollo, e ogni binario lo usa. Gli interi sono sempre big-endian.

Trasporto

Ogni tratta usa Noise_IK_25519_ChaChaPoly_BLAKE2s su TCP. Ogni messaggio Noise è preceduto da una lunghezza di due byte, è lungo al massimo 65.535 byte tag compreso e trasporta solo record interi.

L’identità è un’unica chiave Ed25519. La chiave X25519 che Noise autentica ne è derivata (sk_to_curve25519 di libsodium), e il primo messaggio di handshake dell’iniziatore contiene la sua chiave pubblica Ed25519 di 32 byte, che il rispondente confronta con la chiave dimostrata dall’handshake. Un’unica chiave identifica un’antenna ovunque.

Il preambolo

Prima dell’handshake l’iniziatore invia 16 byte in chiaro. Sono anche il prologo Noise, quindi un byte alterato fa fallire l’handshake, e permettono all’handshake stesso di cambiare in futuro, e a un relay di tenere una chiave vecchia e una nuova durante una rotazione.

ByteCampoSignificato
0–3Numero magicoLe lettere ASCII TALY.
4Versione del preambolo1.
5Handshake1: il pattern Noise descritto sopra.
6–7RiservatoZero.
8–15Indizio della chiaveA quale chiave del rispondente è indirizzata la connessione: i primi 8 byte del BLAKE2s-256 della sua chiave X25519.

Versioni

Poi l’iniziatore invia Hello con la versione di protocollo più bassa e quella più alta che parla, e il suo ruolo. Il rispondente risponde con Welcome e la versione che entrambi parleranno da quel momento, oppure con Goodbye, un motivo e quanto attendere. Dal primo byte a Welcome passano al massimo 15 secondi, e ogni invio successivo ha una scadenza di 30. Finora la versione di protocollo 1 è l’unica.

Un relay accetta per sempre ogni versione di protocollo che un feeder abbia mai distribuito. I feeder installati non si aggiornano, e tutto il resto del protocollo si regge su questa regola.

Record

Un record è un byte di tipo, una lunghezza del corpo di due byte e il corpo. Questi sono i tipi che conosce la versione 1 del protocollo:

TipoRecordCorpo
0x01HelloVersione più bassa, versione più alta (due byte ciascuna), ruolo (un byte: 1 feeder, 2 abbonato, 3 relay, 4 MLAT, un relay che pubblica posizioni).
0x02WelcomeLa versione che entrambi parleranno da qui in avanti (due byte).
0x03BatchIl batch firmato di un ricevitore, descritto più sotto. I relay lo inoltrano byte per byte.
0x04ReceiverInfoLa descrizione firmata che un ricevitore dà di sé: posizione e relativa precisione, classe di clock, software e i relay che ha scelto.
0x05HeartbeatVuoto. Segnale di vita.
0x06RedirectSecondi di attesa (quattro byte) e relay come suggerimenti. Significa solo “vai altrove”; il feeder sceglie comunque secondo le proprie regole.
0x07RelayListI relay che questo relay conosce, offerti come suggerimenti.
0x08RateLimitByte al secondo e burst (quattro byte ciascuno): il massimo che il relay desidera. Un feeder può restare più in basso, mai sopra il limite fissato dal suo proprietario.
0x09GoodbyeUn motivo (un byte) e i secondi di attesa (quattro byte): il relay sta chiudendo.
0x0aSubscribeFlussi (un byte di flag: 1 grezzo, 2 deduplicato, 4 posizioni), un conteggio (due byte) e altrettante celle H3 (otto byte ciascuna), al massimo 64. Sostituisce il Subscribe precedente dell’abbonato.
0x0bRelayBatchUn batch firmato dal relay stesso: il suo flusso deduplicato.
0x0cPositionsPosizioni calcolate dalla MLAT di un relay, firmate da quel relay.

Regole per l’evoluzione

I feeder non si aggiornano mai, quindi queste sono le regole che permettono al protocollo di cambiare senza di loro:

  • Un tipo sconosciuto viene saltato, a meno che il suo bit più alto (0x80) lo marchi come critico, nel qual caso la sessione si chiude in modo pulito. Per questo i nuovi record non sono critici, a meno che ignorarli sia sbagliato.
  • Un corpo può contenere byte dopo i campi che un lettore conosce, e questi vengono ignorati. I campi nuovi si aggiungono in coda.
  • Un lettore conserva un valore di enum che non conosce come il valore Other, mai come errore.
  • Una forma firmata mantiene lo stesso prefisso in ogni versione di formato e termina con la sua firma su tutto ciò che la precede, così un relay può verificare, instradare e deduplicare un formato che per il resto non sa leggere.
  • Il formato di un’osservazione è il byte di tipo di BEAST stesso per i 1090 MHz. L’UAT a 978 MHz usa nuovi valori e non richiede un nuovo record, e i relay inoltrano i formati che non conoscono.

Il batch firmato

Un batch corrisponde a circa un secondo di ricezioni di un singolo ricevitore. Il formato 1 è strutturato così:

ByteCampoSignificato
1Formato1.
32ChiaveLa chiave pubblica Ed25519 del ricevitore.
8ID di avvioCasuale a ogni avvio del feeder.
8Sequenza0, 1, 2 e così via all’interno di un avvio. Un buco a valle corrisponde a dati che una tratta non ha consegnato.
4Epoca del contatoreIncrementata ogni volta che il contatore del ricevitore potrebbe essere ripartito, così i contatori di prima e di dopo non si mescolano mai.
8Orologio di sistemaL’orologio del feeder in millisecondi Unix. Solo informativo: può essere sbagliato.
4ScartateRicezioni scartate dal limite del feeder stesso dal batch precedente.
32PrecedenteL’hash del batch precedente di questo avvio, tutti zeri per il primo, così un ricevitore non può firmare due storie diverse.
4ConteggioQuante osservazioni seguono.
10 + n ciascunaOsservazioniCiascuna è un formato (un byte), il contatore grezzo a 48 bit del ricevitore (sei), il livello del segnale (uno), una lunghezza (due) e altrettanti byte del messaggio così come il decoder lo ha emesso.
64Firma Ed25519Su tutto ciò che la precede.

Formati delle osservazioni: 1 è Mode A/C (2 byte), 2 Mode-S corto (7), 3 Mode-S lungo (14), D un downlink UAT a 978 MHz, U un uplink UAT da terra, ciascuno scritto come il proprio carattere ASCII.

La firma copre il testo ASCII tally batch, un byte zero e ogni byte del batch prima della firma. I batch deduplicati di un relay firmano invece tally relay batch, così l’uno non può mai passare per l’altro.

L’identità di un batch è l’hash BLAKE2s-256 di tutti i suoi byte, firma inclusa. I relay deduplicano in base a essa, e il campo Precedente del batch successivo vi fa riferimento.

I primi 49 byte (formato, chiave, ID di avvio e sequenza) mantengono la loro posizione in ogni formato futuro, e la firma viene sempre per ultima.

Nessuno calibra mai i contatori prima della MLAT: la MLAT di un relay ha bisogno del contatore grezzo per calcolare la soluzione.