Protokół TAK to kontrakt transmisji, którym ATAK, WinTAK, iTAK i TAK Server wymieniają zdarzenia Cursor on Target (CoT). Wersja 0 wysyła zdarzenie jako zwykły XML; wersja 1 — jako komunikat protobuf z prefiksem długości, mniej więcej o połowę mniejszy. Ten sam ładunek jedzie trzema transportami: bezserwerowym UDP multicast po lokalnym meshu, trwałymi strumieniami TCP/TLS do TAK Server (domyślnie port 8089) oraz federacją serwer–serwer (9000/9001).
Jeśli integrujesz sensor, drona albo system C2 z ekosystemem TAK, ten podział ma znaczenie od razu: CoT to model danych — <event>, <point>, <detail> — natomiast protokół TAK definiuje, jak te bajty są ramkowane, wersjonowane i negocjowane na każdym transporcie. Sam schemat XML omawiamy w naszym przewodniku po formacie Cursor on Target i w głębokim nurkowaniu w schemat CoT; ten artykuł to druga połowa zagadnienia: łącze.
CoT a protokół TAK: model danych a kontrakt transmisji
Utrzymuj rozdzielnie trzy warstwy — dokumentacja ekosystemu konsekwentnie je miesza:
- Model zdarzenia CoT. Jeden dokument XML na zdarzenie: kod typu, UID,
time/start/stale, punkt WGS-84 i rozszerzalny<detail>. Zawartość semantyczna, identyczna na wszystkich transportach. - Kodowanie (wersja protokołu TAK). Wersja 0 koduje zdarzenie jako tekst XML; wersja 1 jako protobufowy
TakMessage. Oba niosą te same zdarzenia. - Transport. UDP multicast w meshu, trwały strumień TLS do TAK Server albo łącze federacyjne między serwerami — każdy ramkuje ładunek inaczej.
Opis protokołu dystrybuowany z produktami TAK wyznacza dwie zasady: klient, który wysyła wersję V, potrafi też dekodować wersję V, a każdy klient nadal musi dekodować XML wersji 0. Dlatego mieszane floty po prostu działają — wszystko łagodnie degreduje się do XML.
Protokół TAK wersja 0: czysty CoT XML
Wersja 0 przez pierwszą dekadę nosiła cały ekosystem i do dziś wszędzie jest trybem awaryjnym. Na każdym transporcie zachowuje się inaczej:
- Mesh SA (multicast). Ogłoszenia świadomości sytuacyjnej to datagramy UDP wysyłane na dobrze znaną grupę multicast
239.2.3.1:6969— jedno zdarzenie CoT XML na datagram, bez jakiegokolwiek serwera. Każde urządzenie w grupie dostaje każde zdarzenie, a retransmisji nie ma: zgubiony datagram to zgubiony raport pozycji. To tryb stojący za bezserwerowym blue force tracking w drużynowej sieci Wi-Fi czy MANET. (ATAK multicastytuje GeoChat osobną grupą,224.10.10.1:17012, którą TAK Server może przyjąć jako wejście). - Komunikaty kierunkowe. Wiadomości do jednego odbiorcy jadą krótkotrwałym połączeniem TCP: połącz, wyślij jedno zdarzenie, rozłącz. Urządzenie ATAK nasłuchujące bezpośrednich peerów przyjmuje je na porcie TCP 4242 — poręczne, by wstrzyknąć zdarzenie do pojedynczego telefonu bez serwera.
- Strumień do TAK Server. Trwałe połączenie (zwykle TLS) niesie ciągły strumień zdarzeń XML, każde poprzedzone deklaracją XML i znakiem nowej linii. Odbiorca nie dostaje ani prefiksu długości, ani separatora: ciął strumień, szukając dosłownego tokenu
</event>i odcinając tuż za nim — kolejna deklaracja zaczyna się od zaraz następnego bajtu.
W praktyce forma strumieniowa wygląda tak (skrótowo):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<event version="2.0" uid="RAVEN-1" type="a-f-G-U-C" how="m-g" …>…</event><?xml version="1.0" …?><event …>…</event>
Ten skanujący splitter to zachowanie protokołu, nie dziwactwo implementacji — własny kodek strumieniowy TAK Servera wprost dokumentuje brak separatorów i szuka tagu zamykającego. Każda bramka musi emitować zdarzenia, które tną się czysto: kompletne zdarzenia, deklaracja najpierw, nic pomiędzy. Opisane pełne zdarzenia znajdziesz w naszych przykładach komunikatów CoT.
Protokół TAK wersja 1: protobufowy TakMessage
Wersja 1 zastępuje tekst XML pojedynczym komunikatem proto3 — atakmap.commoncommo.protobuf.v1.TakMessage — którego definicja dołącza się do dystrybucji TAK i jest odzwierciedlona w bibliotekach takich jak pakiet Pythona takproto. Struktura jest zwięzła, lecz ściśle określona:
TakMessagema dwie opcjonalne części:TakControl(obsługa protokołu — dekodowane przez nadawcę wersje min/max plus UID kontaktu) oraz samoCotEvent.CotEventniesie kopertę:type,uid,how, opcjonalneaccess/qos/opex, trzy znaczniki czasu w milisekundach od uniksowej epoki (sendTime,startTime,staleTime) oraz pola punktulat,lon,hae,ce,lejako double. Nieznana wysokość lub błąd to wartownik999999, jak w konwencji XML.Detail— tu robi się ciekawie. Sześć dobrze znanych elementów detail stało się typowanymi podkomunikatami:contact(endpoint, callsign),group(z__group: name, role),precisionLocation(geopointsrc, altsrc),status(battery),takv(device, platform, os, version),track(speed, course). Cała reszta — remarks, treści czatu, elementy wtyczek — upychana jest w jeden stringxmlDetailz surowym XML pozostałych potomków.
Dwie reguły specyfikacji ratują zespoły przed subtelną korupcją danych. Po pierwsze, tylko całe elementy: element detail jest w całości konwertowany na swój typowany komunikat albo w całości pozostaje w xmlDetail — nigdy nie jest dzielony, a jeśli nie mapuje się czysto, zostaje w xmlDetail. Po drugie, odbiorcy scalają komunikaty typowane z powrotem do XML i przy konflikcie wygrywa xmlDetail. Nowe pola wolno dopisywać wyłącznie na końcu istniejących komunikatów; wszystko semantyczne wymaga podbicia wersji. W praktyce oszczędność z protobufa topnieje, gdy rośnie nietypowany detail — dokładnie to widać w ruchu bogatym w czat i wtyczki.
Ramkowanie mesha i strumienia: konkretne bajty
Obie wersje jeżdżą oboma transportami; ramkowanie różni się transportem, nie ładunkiem:
- Datagram mesh:
0xbf, varint wersji protokołu,0xbf, potem ładunek. Magiczny bajt0xbf(191 dziesiętnie) pozwala odbiornikowi powąchać jeden datagram i wiedzieć, czy to protobuf, czy legacy XML zaczynający się od<— wejścia UDP TAK Servera robią dokładnie tę kontrolę na każdym datagramie. - Ramka strumienia:
0xbf, varint długości ładunku w bajtach, potem ładunek. Bajtu wersji brak — wersja została wynegocjowana raz na połączenie (następna sekcja), więc powtarzanie jej w każdej ramce byłoby marnotrawstwem.
Varinty to standardowe bez znaku protobufowe varinty — po 7 bitów, od najmniej znaczących, najwyższy bit oznacza kontynuację, maksimum 64 bity (10 bajtów). Ładunek 258 bajtów koduje się jako 82 02; 127 to 7f, 128 to 80 01.
Ponieważ każda ramka niesie własną długość, parser strumienia to maleńki automat stanów: czekaj na 0xbf, zbierz varint, zbuforuj dokładnie tyle bajtów, zdekoduj, powtórz — przenosząc niekompletne ramki między odczytami. Wersja 0 nie daje nic z tego: skanujesz tekst za </event> i modlisz się, żeby nic nie ucięło w środku zdarzenia.
Negocjacja strumienia: t-x-takp-v, t-x-takp-q, t-x-takp-r
Klient nie może wiedzieć, czy serwer mówi protobufem, więc przełączenie negocjowane jest wewnątrz połączenia trzema zdarzeniami sterującymi CoT — same wysyłane jako XML:
- Oferta (
t-x-takp-v). Serwer wspierający protokół TAK może wysłać jedno zdarzenie tego typu z<detail><TakControl><TakProtocolSupport version="1"/>— po jednym elemencie na wspieraną wersję, najwyżej raz na połączenie, po uwierzytelnieniu, gdy wejście go wymaga. - Żądanie (
t-x-takp-q). Klient wybiera wersję z oferty i odpowiada<TakRequest version="1"/>, po czym musi przestać wysyłać XML (dalej przetwarzając przychodzący XML) i czekać — co najmniej minutę, zanim się podda i połączy ponownie. - Odpowiedź (
t-x-takp-r). Serwer odpowiada<TakResponse status="true"/>albo"false". Przytrueobie strony przełączają całe połączenie na ładunki wersji 1 z prefiksem długości — ta sama wersja w obie strony — i żaden XML już nie płynie. Przyfalsepołączenie zostaje na XML, a klient może spróbować później.
Wszystkie trzy zdarzenia dzielą jeden UID transakcji (protouid ze specyfikacji), a układ jest rygorystyczny: protobuf wysłany przed odpowiedzią true rozrywa połączenie z serwerem referencyjnym. W meshu nie ma uzgadniania: każde urządzenie nadaje komunikat TakControl co najmniej 60 sekund, deklarując minimalną i maksymalną wersję, którą dekoduje; kontakty milczące dwie minuty wracają do wersji swojego ostatniego dekodowalnego komunikatu; a wszyscy nadają na najwyższej wersji, którą dekodować umieją wszyscy znani kontakty, cofając się do XML, gdy tylko pojawi się urządzenie legacy. Przetestuj negocjację pod obciążeniem, zanim uznasz, że flota się przełączyła — dźwignie strojenia opisuje nasz przewodnik po wydajności TAK Servera.
Porty TAK Servera: domyślna mapa
Domyśły z przewodnika konfiguracji TAK Server 5.7 i jego przykładowego CoreConfig.xml; każdy jest konfigurowalny, więc traktuj to jako to, co otwiera fabryczne wdrożenie, a nie umowę. Sama rada przewodnika co do zapory jest minimalna: otwórz 8089 i 8443.
| Port | Protokół | Rola |
|---|---|---|
8089 | TCP/TLS | Wejście strumienia CoT — tu łączą się z certyfikatami klienta ATAK, WinTAK i bramki |
8090 | UDP (QUIC) | Opcjonalne wejście strumienia QUIC, obecne w bieżącej przykładowej konfiguracji |
8087 | TCP lub UDP | Nieszyfrowane wejście CoT (wejścia przykładowe; tylko do laboratorium) |
8088 | TCP | Strumieniowy TCP bez TLS (stcp) — tylko do testów |
8443 | TCP/HTTPS | Panel administracyjny, REST API i WebTAK (uwierzytelnianie certyfikatem klienta) |
8444 | TCP/HTTPS | HTTPS od strony federacji (magazyn zaufania federacji; pobieranie pakietów misji) |
8446 | TCP/HTTPS | Rejestracja certyfikatów (CSR, HTTP Basic auth) i endpoint tokenów OAuth2 |
9000 | TCP/TLS | Nasłuch federacji v1 |
9001 | TCP/TLS | Nasłuch federacji v2 (gRPC po HTTP/2) |
9002 | TCP/TLS | Uwierzytelnianie tokenów federacji (jeśli włączone) |
239.2.3.1:6969 | UDP multicast | Grupa mesh SA (domyślny klient; mostkowalna jako wejście serwera) |
Dwie przypisy. FreeTAKServer — serwer w Pythonie z otwartego ekosystemu (zob. nasz przewodnik po otwartym ekosystemie TAK) — trzyma 8089 dla CoT po TLS, ale używa 8087 dla czystego TCP CoT. A federacja zasługuje na osobną lekturę: łącze v1 wymienia protobufowe komunikaty FederatedEvent za czterobajtowym prefiksem długości, natomiast v2 uruchamia usługę gRPC ze strumieniowymi RPC i kontrolami zdrowia — omawiamy to w naszym przewodniku konfiguracji federacji i w przeglądzie architektury Federation Hub.
Rozmiar komunikatów i pasmo: XML vs protobuf
Zakodowaliśmy oba sposobami dwa reprezentatywne zdarzenia — raport własnej pozycji w stylu ATAK (typowany detail plus nietypowany element uid) i małą ścieżkę drona z mostka MAVLink, przy czym stronę protobufową kodował referencyjny enkoder Pythona:
| Zdarzenie | CoT XML | XML w strumieniu (z deklaracją) | Ładunek protobuf | Na łączu (w ramce) |
|---|---|---|---|---|
| Raport własnej pozycji ATAK | 578 B | 634 B | 258 B | 261 B |
| Ścieżka z mostka BSP | 363 B | 419 B | 171 B | 174 B |
To 52–59% oszczędności dla takich kształtów — zgodne z 55–65%, które podawaliśmy w naszym artykule o integracji telemetrii dronów, i z pomiarami polowymi Isode z 2025 roku dla TAK po radiu HF: mniej więcej 530-bajtowe zdarzenia XML kontra 376–380-bajtowe federacyjne komunikaty protobuf przed kompresją. Protobuf pomaga najbardziej tam, gdzie XML to czysta koperta — deklaracje, nazwy atrybutów, tagi zamykające — a detail zaparkowany w xmlDetail zachowuje swój rozmiar XML.
Dlaczego to się liczy na radiu: czterdzieści osób raportujących się co dziesięć sekund kosztuje około 20 kbps w strumieniowym XML (po 634 B) kontra około 8 kbps w protobuf, zanim doliczy się narzut rekordu TLS na komunikat. Na wąskim paśmie efekt jest brutalny — przy 75 bps Isode zmierzyła 29–33 sekund opóźnienia end-to-end dla pojedynczego zdarzenia. Jeszcze jedno zachowanie rozmiarowe: wyjście strumieniowe TAK Servera odmawia wysłania protobufowego TakMessage większego niż 65 536 bajtów; przeroszone zdarzenie zastępuje mały wskaźnik transferu pliku każący klientowi pobrać pełny XML po HTTPS. Ruch czatowy rozkładamy w naszym artykule o strategii danych czatu taktycznego.
Wysyłanie CoT z Pythona: TLS, certyfikaty, protobuf
Wszystko powyżej zamyka się w małym programie — sama biblioteka standardowa potrafi otworzyć połączenie TLS do wejścia 8089 z certyfikatem klienta i puszczać strumień zdarzeń XML wersji 0, bez zależności:
import asyncio
import datetime as dt
import ssl
import xml.etree.ElementTree as ET
TAK_HOST, TAK_PORT = "tak.example.org", 8089 # TLS streaming input
CA_FILE, CERT_FILE, KEY_FILE = "takserver-ca.pem", "client.pem", "client.key"
def ts(t): # CoT time: ISO 8601, UTC, millisecond precision
return t.strftime("%Y-%m-%dT%H:%M:%S.") + f"{t.microsecond // 1000:03d}Z"
def cot_event(uid, cot_type, lat, lon, hae, callsign, stale_s=60):
now = dt.datetime.now(dt.timezone.utc)
ev = ET.Element("event", version="2.0", uid=uid, type=cot_type, how="m-g",
time=ts(now), start=ts(now),
stale=ts(now + dt.timedelta(seconds=stale_s)))
ET.SubElement(ev, "point", lat=f"{lat:.7f}", lon=f"{lon:.7f}",
hae=f"{hae:.1f}", ce="10.0", le="9999999.0")
ET.SubElement(ET.SubElement(ev, "detail"), "contact", callsign=callsign)
return ET.tostring(ev, encoding="utf-8", xml_declaration=False)
async def drain(reader):
# Keep reading the server (its t-x-takp-v offer, other users' SA),
# or TCP flow control eventually stalls its writes to you.
while await reader.read(65536):
pass
async def main():
ctx = ssl.create_default_context(cafile=CA_FILE) # verify server cert + name
ctx.load_cert_chain(CERT_FILE, KEY_FILE) # our client identity
reader, writer = await asyncio.open_connection(TAK_HOST, TAK_PORT, ssl=ctx)
rx = asyncio.create_task(drain(reader))
try:
while not rx.done():
writer.write(cot_event("gw-demo.uav-12", "a-f-A-M-F-Q",
50.4501234, 30.5234567, 640.0, "UAV-12"))
await writer.drain()
await asyncio.sleep(5) # report well inside stale
finally:
writer.close()
asyncio.run(main())
Trzy szczegóły niosą tu cały ciężar. Gniazda asyncio domyślnie wyłączają algorytm Nagle'a, więc każde zdarzenie wychodzi natychmiast — na surowych gniazdach ustaw TCP_NODELAY samodzielnie. Zadanie drain nie jest ozdobą: serwer piszący do klienta, który nigdy nie czyta, w końcu zawiśnie na kontroli przepływu TCP. Certyfikaty muszą być PEM: TAK Server wystawia PKCS#12 .p12 (to, co konsumuje ATAK), a OpenSSL zamienia go w trzy linie:
openssl pkcs12 -in gateway-01.p12 -clcerts -nokeys -out client.pem -passin pass:SECRET
openssl pkcs12 -in gateway-01.p12 -nocerts -nodes -out client.key -passin pass:SECRET
openssl pkcs12 -in truststore-root.p12 -nokeys -out takserver-ca.pem -passin pass:SECRET
Po stronie protobuf pakiet takproto (PyPI) konwertuje CoT XML na ładunki wersji 1 i z powrotem oraz dokłada ramkowanie; biblioteka PyTAK opakowuje całego klienta — COT_URL przyjmuje tls://host:8089 albo udp+wo://239.2.3.1:6969, TAK_PROTO=1 wybiera protobuf, a certyfikaty importują się prosto z pakietu danych TAK .zip. Kalkulację „zbuduj sam czy weź bibliotekę" rozpisujemy w naszym artykule o wzorcach integracji C2 z TAK.
Właśnie tu prototyp z weekendu spotyka rzeczywistość operacyjną: rejestracja certyfikatów w skali floty, negocjacja protobuf między mieszanymi wersjami klientów, multicast, który faktycznie ma się routować. Budujemy bramki CoT/protokołu TAK, migrujemy strumienie z XML do protobufa, integrujemy i utwardzamy TAK Server. Napisz, co musisz podłączyć →
Semantyka dostarczania, bezpieczeństwo i pułapki, które gryzą
Brak potwierdzeń — robotę odrabia stale
CoT to fire-and-forget na każdej warstwie: bez potwierdzeń per komunikat, bez numerów sekwencyjnych, bez sesji. Świeżością zarządza znacznik stale — konsumenci starzeją lub gubią zdarzenia po jego przekroczeniu — oraz kadencją: wysyłaj wyraźnie wewnątrz własnego okna stale albo pogódź się z duchami na mapie. TAK Server łagodzi ponowne połączenia buforem latest SA (nowi klienci od razu dostają ostatnie znane pozycje osiągalnych użytkowników), a ATAK trzyma połączenie przy życiu pingiem (t-x-c-t), na który serwer odpowiada pongiem (t-x-c-t-r). Po każdym reconnect prześlij od nowa swój aktualny obraz, zamiast zakładać, że serwer go zapamiętał.
Certyfikaty, grupy, kanały
Ruch operacyjny TAK to wzajemny TLS: klient weryfikuje serwer względem CA wdrożenia, serwer weryfikuje certyfikat klienta z tego samego CA, a CN certyfikatu staje się nazwą użytkownika do przydziału grup. Członkostwo w grupach — per wejście, per certyfikat albo przez backendy LDAP/plikowe — to cały model kontroli dostępu do tego, czyje ślady kto widzi; niedopasowane połączenia lądują w __ANON__. Federacja celowo używa osobnego magazynu zaufania, więc CA partnera nigdy po cichu nie autoryzuje połączeń klienckich, a rejestracja certyfikatów biegnie na porcie 8446 za HTTP Basic auth. Gdy wdrożenie potrzebuje API HTTP zamiast surowych gniazd, tę powierzchnię opisuje nasz przewodnik integracji API CloudTAK.
Powracające tryby awarii
- Multicast, który nie dociera: multicast idzie dalej tylko, póki pozwala TTL (domyślny TTL 1 trzyma datagramy w lokalnym segmencie; przykładowa konfiguracja TAK Servera podnosi go do 5), źle skonfigurowany IGMP snooping po cichu wywala grupy, a multicast nigdy nie przechodzi przez NAT. Mesh SA to technika jednej podsieci.
- Dryf zegara: przy starzeniu po stale zły zegar albo wyparowuje czyjeś ślady, albo zostawia je na zawsze. Zsynchronizuj czas (GNSS lub NTP), zanim zaczniesz debugować cokolwiek innego.
- Tarcie formatów certyfikatów: serwer mówi JKS, klienci PKCS#12, twoja usługa PEM — a certyfikaty bez rozszerzonego użycia klucza client-auth są odrzucane już w handshake. Konwertuj świadomie i testuj dokładny łańcuch.
- Algorytm Nagle'a: opóźnienie batchowania 200 ms jest niewidoczne na łączu do data center, a śmiertelne na taktycznym. Wyłącz go albo batchuj jawnie.
- Burze reconnectów: sto bramek łączących się naraz po przerwie radiowej wygląda jak DDoS. Użyj jitterowanego wykładniczego backoff, uwierzytelnij się ponownie, odtwórz aktualny stan.
- Założenia o mieszanych wersjach: jeden legacy klient w meshu spycha wszystkich z powrotem do XML, a serwer, który nigdy nie oferuje
t-x-takp-v, nigdy nie dostanie protobufa. Instrumentuj, jakim kodowaniem ustabilizowały się twoje łącza — bajty na łączu na minutę mówią prawdę.
Architektura referencyjna: usługa bramki CoT
Trwały wzorzec zasilania sieci TAK z systemów nie-TAK to mała, dedykowana usługa bramki, a nie kod wpleciony w każde źródło:
- Adaptery wejściowe — MAVLink, radar, AIS, trackery GPS, strumienie REST/WebSocket — każdy normalizowany do wewnętrznego modelu ścieżki.
- Builder CoT — jedno miejsce właściciela polityki UID (stabilne, przestrzenne nazwy, odporne na kolizje), mapowania typów, kodów
howi okien stale per źródło, emitujące XML albo protobuf według tych samych reguł detail. - Nadzorca wyjścia — pula uwierzytelnionych połączeń TLS z negocjacją (protobuf, gdy oferowany, inaczej XML), jitterowanym reconnectem, kształtowaniem tempa per łącze dla ścieżek wąskopasmowych i odtwarzaniem aktualnego stanu.
- Powierzchnia operacyjna — zdrowie łączy, używane kodowanie, bajty na minutę i monitoring wygaśnięć certyfikatów; w sieciach taktycznych protokół jest pierwszym podejrzanym i ostatnim mierzonym.
Taki kształt — protokołowo wierna krawędź między cudzymi danymi a TAK Serverem — to komponent, który budujemy dla programów obronnych, ramię w ramię z kopilotem TAKpilot na tej samej infrastrukturze. Jeśli twój program rysuje właśnie ten diagram, weź zespół, który już to wysłał w pole.
Potrzebujesz tego w swojej sieci?
Budujemy bramki protokołu CoT/TAK, migrujemy strumienie z XML do protobufa, integrujemy i utwardzamy TAK Server — z certyfikatami, grupami i federacją w pakiecie.
Przygotowali inżynierowie Corvus Intelligence — zespołu, który buduje bramki CoT/TAK, wtyczki ATAK i integracje TAK Servera dla programów obronnych (certyfikacja ISO 9001/27001, członek Brave1). O Corvus Intelligence →