Features

Everything you need,
nothing you don't

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.

πŸ“
Feature 01

CoT Event Construction

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.

Key behaviors

  • βœ“ Validates lat ∈ [-90, 90] and lon ∈ [-180, 180]
  • βœ“ Substitutes 9999999.0 for NULL hae / ce / le fields
  • βœ“ Rejects stale ≀ time (CoT requirement)
  • βœ“ 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.

SQLCoT event construction
-- 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;
πŸ“„
Feature 02

CoT XML Serialization

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.

Compliance guarantees

  • βœ“XML declaration on every message
  • βœ“All timestamps as ISO 8601 with ms precision + Z suffix
  • βœ“<point> child with lat, lon, hae, ce, le attributes
  • βœ“Optional <detail> block with user-defined sub-elements
  • βœ“Round-trip property: parse β†’ re-serialize produces identical output
SQLSerialize to XML
SELECT 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')
  )
);
XMLOutput
<?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>
βš™οΈ
Feature 03

TAK Protocol Buffers

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.

Wire format

textTAK framing
Byte 0:     0xbf       (magic byte)
Bytes 1-N:  varint     (protobuf payload length)
Bytes N+1+: CotEvent   (protobuf-encoded)
SQLSwitch to protobuf
-- 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');
βš™οΈ
Feature 04

TAK Server Configuration

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.

SQLConfigure mTLS
-- 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();
πŸ”Œ
Feature 05

Connection Management

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.

Supported transports

βœ“ Supported tcp Plain TCP socket
βœ“ Supported tcp+tls TLS with optional mTLS
Planned 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.

SQLConnection lifecycle
-- 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.
⚑
Feature 06

Message Transmission

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.

SQLBatch send with error reporting
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
πŸ—ΊοΈ
Feature 07

GIS / Geospatial Translation

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 TypeCoT lat/lonDetail encoding
POINTX=lon, Y=latNone
LINESTRINGCentroid<shape> with WKT
POLYGONCentroid<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().

SQLGIS β†’ CoT β†’ TAK
-- 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'
);
πŸ“‹
Feature 08

Runtime Logging

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.

INFO Successful sends, connections, configuration changes
WARN Non-fatal conditions (e.g. batch partial failure)
ERROR Validation failures, I/O errors, TLS errors
πŸ’‘

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.

SQLInspect runtime log
-- 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();