tak-cot-sender is organized into eight focused functional areas. Each area exposes a small, composable set of SQL functions. Combine them in a single query to build complete end-to-end situational awareness pipelines.
Three scalar functions collaborate to construct a fully-validated
CoT event from SQL values. The result is a DuckDB STRUCT
that flows naturally through any subsequent serialization or
transmission function.
lat β [-90, 90] and lon β [-180, 180]
9999999.0 for NULL hae / ce / le fields
cot_type() builds MIL-STD-2525B dot-notation strings
cot_detail() takes variadic key-value pairs
The returned STRUCT is immutable and composable β pass it directly
to cot_to_xml(), tak_send(), or
struct_update() to add detail fields on the fly.
-- Minimal event
SELECT cot_event(
'UNIT-001', -- uid
cot_type('a', 'f', 'G-U-C'), -- type
34.0522, -118.2437, -- lat, lon
71.0, 10.0, 10.0, -- hae, ce, le
TIMESTAMPTZ '2026-01-15 12:00:00+00',
TIMESTAMPTZ '2026-01-15 12:00:00+00',
TIMESTAMPTZ '2026-01-15 12:05:00+00'
);
-- With callsign detail, added via struct_update
SELECT struct_update(
cot_event(...),
detail := cot_detail('callsign', 'ALPHA-1')
);
-- Build events directly from a table
SELECT cot_event(
unit_id,
cot_type(affiliation, battle_dim, fn_id),
lat, lon, altitude,
NULL, NULL, -- ce/le β 9999999
now(), now(),
now() + INTERVAL '5 min'
)
FROM units;
cot_to_xml() encodes any CoT event struct to a
UTF-8 XML string that fully conforms to the CoT base schema
(version 2.0). No external XML library is required β output is
assembled via an efficient string builder.
The output is accepted by all TAK clients including ATAK, WinTAK, iTAK, WebTAK, and any legacy CoT-compatible receiver going back to the original MITRE specification.
<point> child with lat, lon, hae, ce, le attributes<detail> block with user-defined sub-elementsSELECT cot_to_xml(
struct_update(
cot_event(
'SENDER-001', cot_type('a','f','G-U-C'),
34.0522, -118.2437, 71.0,
10.0, 10.0,
TIMESTAMPTZ '2026-01-15 12:00:00+00',
TIMESTAMPTZ '2026-01-15 12:00:00+00',
TIMESTAMPTZ '2026-01-15 12:05:00+00'
),
detail := cot_detail('callsign', 'ALPHA-1')
)
);
<?xml version="1.0" encoding="UTF-8"?>
<event version="2.0"
uid="SENDER-001"
type="a-f-G-U-C"
time="2026-01-15T12:00:00.000Z"
start="2026-01-15T12:00:00.000Z"
stale="2026-01-15T12:05:00.000Z"
how="m-g">
<point lat="34.0522" lon="-118.2437"
hae="71.0" ce="10.0" le="10.0"/>
<detail><callsign>ALPHA-1</callsign></detail>
</event>
Modern ATAK and WinTAK clients prefer Protocol Buffers over XML
for lower bandwidth consumption in constrained environments.
cot_to_protobuf() returns a BLOB with the TAK protocol
framing header prepended.
Protobuf serialization is designed but not yet fully implemented in
the current MVP. tak_set_format('protobuf') is accepted,
but actual binary encoding is a planned follow-on item.
Byte 0: 0xbf (magic byte)
Bytes 1-N: varint (protobuf payload length)
Bytes N+1+: CotEvent (protobuf-encoded)
-- Switch format for the session
SELECT tak_set_format('protobuf');
-- tak_send now encodes as TAK protobuf
SELECT tak_send(
cot_event(...)
);
-- Or serialize explicitly to a BLOB
SELECT cot_to_protobuf(
cot_event(...)
) AS raw_protobuf_bytes;
-- Switch back to XML
SELECT tak_set_format('xml');
Connection settings β including PEM certificate material for mTLS β
are persisted in a DuckDB-managed table (__tak_config).
This means configuration survives session restarts without re-entry.
tak_configure(host, port, protocol)
Sets host, port, and transport. Protocol must be tcp, tcp+tls, or udp.
tak_configure_tls(client_cert, client_key, ca_cert)
Stores PEM strings for mTLS. Displayed as truncated (first 32 chars) in tak_config_show().
tak_config_show()
Returns all settings as a table. Certificate fields are masked for security.
-- Basic TCP+TLS
SELECT tak_configure(
'tak.example.mil', 8089, 'tcp+tls'
);
-- Add mTLS certificate material
SELECT tak_configure_tls(
read_text('client.pem'), -- client cert
read_text('client.key'), -- client key
read_text('ca.pem') -- CA cert
);
-- Inspect current settings
SELECT * FROM tak_config_show();
-- key | value
-- -------------|-----------------------------
-- host | tak.example.mil
-- port | 8089
-- protocol | tcp+tls
-- client_cert | -----BEGIN CERTIFICATE-----...
-- Clear everything
SELECT tak_config_clear();
One active connection is maintained per DuckDB session. The Connection Manager handles the full TCP/TLS lifecycle including the OpenSSL mTLS handshake, connection state tracking, and resource cleanup.
tcp
Plain TCP socket
tcp+tls
TLS with optional mTLS
udp
UDP multicast (roadmap)
When TLS handshake fails, the error message includes the certificate subject DN and the exact OpenSSL verification failure reason β no guesswork when debugging cert issues.
-- Connect (uses active config)
SELECT tak_connect();
-- β 'connected to tak.example.mil:8089 (tcp+tls)'
-- Check status anytime
SELECT tak_connection_status();
-- β {state: 'connected', host: 'tak.example.mil',
-- port: 8089, protocol: 'tcp+tls',
-- last_error: NULL}
-- Disconnect and release socket
SELECT tak_disconnect();
-- Error if not configured first
SELECT tak_connect();
-- β Error: no TAK configuration found.
-- Call tak_configure() first.
Two transmission functions cover the full range of send patterns β single events for low-latency real-time feeds and batched arrays for bulk dataset pushes.
tak_send(event)
β BIGINT
Sends a single CoT event. Returns the number of bytes transmitted.
tak_send_batch(events[])
β TABLE
Sends a list of events. Returns a result row per event with index, uid, status, and error_message. Errors on one row never abort the rest.
SELECT *
FROM tak_send_batch([
cot_event('BATCH-001', 'a-f-G-U-C',
36.0, -116.0, 50.0, 5.0, 5.0,
TIMESTAMPTZ '2026-01-15 14:00:00+00',
TIMESTAMPTZ '2026-01-15 14:00:00+00',
TIMESTAMPTZ '2026-01-15 14:05:00+00'
),
cot_event('BATCH-002', 'a-f-G-U-C',
37.0, -115.0, 60.0, 6.0, 6.0,
TIMESTAMPTZ '2026-01-15 14:01:00+00',
TIMESTAMPTZ '2026-01-15 14:01:00+00',
TIMESTAMPTZ '2026-01-15 14:06:00+00'
)
]);
-- index | uid | status | error_message
-- ------|-----------|--------|---------------
-- 0 | BATCH-001 | ok | NULL
-- 1 | BATCH-002 | ok | NULL
The GIS Translator bridges the DuckDB spatial extension with TAK, enabling you to publish any vector layer β feature services, GeoJSON files, PostGIS queries β directly to the common operating picture.
| Geometry Type | CoT lat/lon | Detail encoding |
|---|---|---|
POINT | X=lon, Y=lat | None |
LINESTRING | Centroid | <shape> with WKT |
POLYGON | Centroid | <shape> with WKT |
Works with any data source the spatial extension can read:
Shapefiles, GeoPackage, GeoJSON, PostGIS, S3-hosted vector tiles,
and anything readable via ST_Read().
-- Convert a single geometry
SELECT cot_from_geometry(
ST_Point(-118.2437, 34.0522),
'GEOM-001', -- uid
'a-f-G-U-C' -- type
);
-- Publish an entire feature layer
SELECT * FROM tak_send_features(
$$
SELECT geom, asset_id, cot_type
FROM ST_Read('assets.geojson')
WHERE status = 'active'
$$,
'asset_id', -- uid column
'cot_type' -- type column
);
-- Load live GIS data from a featureserv endpoint
SELECT tak_send(
cot_from_geometry(geom, id, 'a-h-G')
)
FROM read_json_auto(
'https://featureserv.example.mil/collections/vehicles/items'
);
A thread-safe in-memory ring-buffer records every operation β successful sends, connection events, configuration changes, and errors. Query it with standard SQL at any time.
The ring-buffer holds up to 10,000 entries. Oldest entries are silently discarded when the limit is exceeded β no memory growth, no configuration required.
-- Last 10 entries
SELECT * FROM tak_log(10);
-- timestamp | level | operation | message
-- ----------|-------|-----------|-------------------
-- 12:00:01 | INFO | tak_send | uid=UNIT-001 248B
-- 12:00:02 | INFO | tak_send | uid=UNIT-002 248B
-- 12:00:03 | ERROR | tak_send | lat out of range
-- Filter for errors only
SELECT *
FROM tak_log(1000)
WHERE level = 'ERROR'
ORDER BY timestamp DESC;
-- Default: last 100 entries
SELECT * FROM tak_log();