Das Protokoll

Das muss ein Feeder, Relay oder Client sprechen, um mit den anderen zusammenzuarbeiten. Die Referenz ist das wire-Crate im Tally-Repository, der einzige Code dort, der das Protokoll kodiert oder dekodiert, und jedes Binary nutzt es. Ganzzahlen sind durchgehend Big-Endian.

Transport

Jeder Hop ist Noise_IK_25519_ChaChaPoly_BLAKE2s über TCP. Jede Noise-Nachricht wird hinter einer Länge aus zwei Bytes gesendet, höchstens 65.535 Bytes einschließlich Tag, und trägt nur vollständige Records.

Die Identität ist ein einziger Ed25519-Schlüssel. Der X25519-Schlüssel, den Noise authentifiziert, wird daraus abgeleitet (sk_to_curve25519 von libsodium), und die erste Handshake-Nachricht des Initiators trägt seinen öffentlichen Ed25519-Schlüssel (32 Bytes), den der Responder gegen den Schlüssel prüft, den der Handshake bewiesen hat. Ein Schlüssel benennt eine Antenne überall.

Die Präambel

Vor dem Handshake sendet der Initiator 16 Bytes im Klartext. Sie sind zugleich der Noise-Prolog, sodass ein verändertes Byte den Handshake scheitern lässt. Und sie erlauben, den Handshake selbst später zu ändern und ein Relay während eines Schlüsselwechsels einen alten und einen neuen Schlüssel halten zu lassen.

BytesFeldBedeutung
0–3MagicDie ASCII-Buchstaben TALY.
4Präambel-Version1.
5Handshake1: das Noise-Muster oben.
6–7ReserviertNull.
8–15SchlüsselhinweisAuf welchen der Schlüssel des Responders die Verbindung gerichtet ist: die ersten 8 Bytes von BLAKE2s-256 über seinen X25519-Schlüssel.

Versionen

Danach sendet der Initiator Hello mit der niedrigsten und der höchsten Wire-Version, die er spricht, und seiner Rolle. Der Responder antwortet mit Welcome und der Version, die beide von da an sprechen, oder mit Goodbye, einem Grund und einer Wartezeit. Vom ersten Byte bis Welcome vergehen höchstens 15 Sekunden, und jedes Senden danach hat eine Frist von 30. Wire-Version 1 ist bisher die einzige.

Ein Relay beantwortet jede Wire-Version, die je ein Feeder ausgeliefert hat, für immer. Installierte Feeder aktualisieren sich nicht, und der Rest des Protokolls beruht auf dieser Regel.

Records

Ein Record besteht aus einem Typ-Byte, einer Inhaltslänge aus zwei Bytes und dem Inhalt. Diese Typen kennt Wire-Version 1:

TypRecordInhalt
0x01HelloNiedrigste Version, höchste Version (je zwei Bytes), Rolle (ein Byte: 1 Feeder, 2 Abonnent, 3 Relay, 4 MLAT, ein Relay, das Positionen veröffentlicht).
0x02WelcomeDie Version, die beide ab jetzt sprechen (zwei Bytes).
0x03BatchDer signierte Batch eines Empfängers, siehe unten. Relays leiten ihn Byte für Byte weiter.
0x04ReceiverInfoDie signierte Selbstbeschreibung eines Empfängers: Standort und dessen Genauigkeit, Uhrenklasse, Software und die Relays, die er gewählt hat.
0x05HeartbeatLeer. Lebenszeichen.
0x06RedirectWartezeit in Sekunden (vier Bytes) und Relays als Hinweise. Es heißt nur „verlass mich“; der Feeder wählt weiterhin nach seinen eigenen Regeln.
0x07RelayListRelays, die dieses Relay kennt, als Hinweise angeboten.
0x08RateLimitBytes pro Sekunde und Burst (je vier Bytes): das Höchste, was das Relay möchte. Ein Feeder darf darunter bleiben, aber nie über die Obergrenze seines Betreibers gehen.
0x09GoodbyeEin Grund (ein Byte) und eine Wartezeit in Sekunden (vier Bytes): Das Relay schließt.
0x0aSubscribeStreams (ein Byte Flags: 1 roh, 2 dedupliziert, 4 Positionen), eine Anzahl (zwei Bytes) und so viele H3-Zellen (je acht Bytes), höchstens 64. Ersetzt das vorherige Subscribe des Abonnenten.
0x0bRelayBatchEin Batch, den das Relay selbst signiert hat: sein deduplizierter Stream.
0x0cPositionsPositionen, die das MLAT eines Relays berechnet hat, von diesem Relay signiert.

Regeln für Änderungen

Feeder werden nie aktualisiert, also sind dies die Regeln, nach denen sich das Protokoll ohne sie ändern kann:

  • Ein unbekannter Typ wird übersprungen, außer sein höchstes Bit (0x80) markiert ihn als kritisch, was die Sitzung sauber beendet. Neue Records sind daher nicht kritisch, es sei denn, sie zu ignorieren wäre falsch.
  • Ein Inhalt darf nach den Feldern, die ein Leser kennt, weitere Bytes tragen, und diese werden ignoriert. Felder kommen hinzu, indem sie angehängt werden.
  • Ein Enum-Wert, den ein Leser nicht kennt, behält er als den Wert Other, nie als Fehler.
  • Eine signierte Form behält in jeder Formatversion dasselbe Präfix und endet mit ihrer Signatur über alles davor, sodass ein Relay ein Format prüfen, routen und deduplizieren kann, das es sonst nicht liest.
  • Das Format einer Beobachtung ist für 1090 MHz das Typ-Byte von BEAST selbst. 978-MHz-UAT bekommt neue Werte und braucht keinen neuen Record, und Relays leiten Formate weiter, die sie nicht kennen.

Der signierte Batch

Ein Batch umfasst etwa eine Sekunde an Empfangsdaten eines Empfängers. Format 1 ist so aufgebaut:

BytesFeldBedeutung
1Format1.
32SchlüsselDer öffentliche Ed25519-Schlüssel des Empfängers.
8Boot-IDBei jedem Start des Feeders zufällig.
8Sequenz0, 1, 2 und so weiter innerhalb eines Boots. Eine Lücke weiter hinten in der Kette sind Daten, die ein Hop nicht zugestellt hat.
4ZählerepocheWird erhöht, wann immer der Zähler des Empfängers neu gestartet sein könnte, sodass Zählerstände von davor und danach nie zusammentreffen.
8UhrzeitDie Uhr des Feeders in Unix-Millisekunden. Nur zur Information: Sie kann falsch gehen.
4VerworfenEmpfangsdaten, die die eigene Obergrenze des Feeders seit dem vorherigen Batch verworfen hat.
32VorgängerDer Hash des vorherigen Batches dieses Boots, Nullen für den ersten, sodass ein Empfänger keine zwei Verläufe signieren kann.
4AnzahlWie viele Beobachtungen folgen.
je 10 + nBeobachtungenJede besteht aus einem Format (ein Byte), dem rohen 48-Bit-Zähler des Empfängers (sechs), dem Signalpegel (eins), einer Länge (zwei) und entsprechend vielen Bytes der Nachricht, so wie der Decoder sie ausgegeben hat.
64Ed25519-SignaturÜber alles davor.

Beobachtungsformate: 1 ist Mode A/C (2 Bytes), 2 Mode-S kurz (7), 3 Mode-S lang (14), D ein 978-MHz-UAT-Downlink, U ein UAT-Uplink vom Boden, jeweils als ASCII-Zeichen geschrieben.

Die Signatur umfasst den ASCII-Text tally batch, ein Null-Byte und jedes Byte des Batches vor der Signatur. Die eigenen deduplizierten Batches eines Relays signieren stattdessen tally relay batch, sodass der eine nie als der andere durchgehen kann.

Die Identität eines Batches ist der BLAKE2s-256-Hash aller seiner Bytes, Signatur eingeschlossen. Relays deduplizieren danach, und das Feld Vorgänger des nächsten Batches nennt ihn.

Die ersten 49 Bytes (Format, Schlüssel, Boot-ID und Sequenz) behalten ihren Platz in jedem künftigen Format, und die Signatur kommt immer zuletzt.

Zähler werden vor dem MLAT nie kalibriert, von niemandem: Das MLAT eines Relays braucht den rohen Zähler, um zu rechnen.