Prerequisites
You need these tools installed before building tak-cot-sender.
The runtime that hosts the extension. Download from duckdb.org/docs/installation/
Build system. Install via system package manager or cmake.org
C++17 required. Windows: Visual Studio Build Tools.
Required for TCP+TLS transport. Windows: use vcpkg or the OpenSSL installer.
Easiest way to install OpenSSL on Windows: vcpkg install openssl:x64-windows
Required to clone the repository and its CMake dependencies.
Build the Extension
Clone the repository and run the standard DuckDB extension CMake build.
# Clone
git clone --recurse-submodules https://github.com/your-org/tak-cot-sender.git
cd tak-cot-sender
# Configure (vcpkg provides OpenSSL)
cmake -B build -S . `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-DVCPKG_TARGET_TRIPLET=x64-windows `
-DCMAKE_BUILD_TYPE=Release
# Build
cmake --build build --config Release --parallel
# Output: build/Release/tak_cot_sender.duckdb_extension
# Install OpenSSL dev headers (Debian/Ubuntu)
sudo apt-get install -y libssl-dev
# Clone
git clone --recurse-submodules https://github.com/your-org/tak-cot-sender.git
cd tak-cot-sender
# Configure & Build
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel $(nproc)
# Output: build/tak_cot_sender.duckdb_extension
Load into DuckDB
Use DuckDB's LOAD command with the path to the built .duckdb_extension file. For community extensions not yet in the official registry, use the unsigned load path.
-- Allow unsigned extensions (community build)
SET allow_unsigned_extensions = 'true';
-- Windows path example
LOAD 'C:/tak-cot-sender/build/Release/tak_cot_sender.duckdb_extension';
-- Linux path example
LOAD '/opt/tak-cot-sender/build/tak_cot_sender.duckdb_extension';
-- Verify load was successful
SELECT * FROM duckdb_extensions() WHERE extension_name LIKE '%tak%';
Add the LOAD and SET statements to a .duckdbrc file in your home directory to auto-load the extension on every DuckDB session.
Configure TAK Server
Store your TAK Server endpoint. Configuration is persisted to an in-memory table __tak_config for the lifetime of the DuckDB session.
-- Plain TCP (development / non-TLS environments)
SELECT tak_configure('192.168.1.100', 8087, 'tcp');
-- Verify the config was stored
SELECT * FROM tak_config_show();
Common TAK Server ports
8087 β TCP (plain)8088 β TCP + TLS8089 β TCP + mTLS (cert auth)8080 β HTTP CoT ingestBuild Your First CoT Event
Construct and inspect a CoT event before sending it to verify the XML output.
Construct the event struct
SELECT cot_event(
'UNIT-ALPHA-001', -- uid
cot_type('a', 'f', 'G-U-C'), -- type: friendly ground combat unit
34.0522, -- lat (Los Angeles)
-118.2437, -- lon
71.0, -- hae (m)
10.0, -- ce (m)
10.0, -- le (m)
now(), -- time
now(), -- start
now() + INTERVAL '5 minutes' -- stale
) AS event;
Add optional detail metadata
SELECT struct_pack(
uid := 'UNIT-ALPHA-001',
type := cot_type('a', 'f', 'G-U-C'),
lat := 34.0522,
lon := -118.2437,
hae := 71.0,
ce := 10.0,
le := 10.0,
event_time := now(),
start := now(),
stale := now() + INTERVAL '5 minutes',
detail := cot_detail(
'callsign', 'ALPHA-1',
'battery', '87',
'group', 'Cyan',
'role', 'Team Member'
)
);
Preview the XML output
SELECT cot_to_xml(cot_event(
'UNIT-ALPHA-001', cot_type('a','f','G-U-C'),
34.0522, -118.2437, 71.0, 10.0, 10.0,
now(), now(), now() + INTERVAL '5 minutes'
));
The output is a complete UTF-8 XML string you can paste into any CoT validator or send manually to verify the structure before wiring up the TAK connection.
Connect & Send
Establish the connection and transmit the event.
-- 1. Connect (uses the tak_configure settings)
SELECT tak_connect();
-- 2. Verify the connection
SELECT * FROM tak_connection_status();
-- 3. Send a single event β returns bytes written
SELECT tak_send(cot_event(
'UNIT-ALPHA-001', cot_type('a','f','G-U-C'),
34.0522, -118.2437, 71.0, 10.0, 10.0,
now(), now(), now() + INTERVAL '5 minutes'
));
-- 4. Check the log for errors
SELECT * FROM tak_log();
-- 5. Disconnect when done
SELECT tak_disconnect();
Batch Transmission
Send many events at once β perfect for a query result set like a patrol route, asset registry, or geofence boundary.
-- Build events from a table of assets
CREATE TABLE assets AS (
SELECT 'BRAVO-1' AS uid, 34.052 AS lat, -118.24 AS lon UNION ALL
SELECT 'CHARLIE-1', 34.060, -118.30 UNION ALL
SELECT 'DELTA-1', 34.045, -118.20
);
-- Send batch β returns per-event status rows
SELECT *
FROM tak_send_batch(
SELECT list(cot_event(
uid, cot_type('a','f','G-U-C'),
lat, lon, NULL, NULL, NULL,
now(), now(), now() + INTERVAL '5 min'
)) FROM assets
);
TLS & mTLS Setup
Production TAK Servers require TLS, and most require mutual TLS with client certificates issued by the TAK Server CA.
-- Step 1: Configure the server endpoint with TLS
SELECT tak_configure('tak.mission.mil', 8089, 'tcp+tls');
-- Step 2: Load certificate material from PEM files
SELECT tak_configure_tls(
read_text('/certs/client.pem'), -- client certificate
read_text('/certs/client.key'), -- private key (PEM)
read_text('/certs/ca-chain.pem') -- CA certificate chain
);
-- Step 3: Connect β performs full mTLS handshake
SELECT tak_connect();
Key handling: Private key PEM content is held in memory only for the session and never written to disk by the extension. Treat the DuckDB process as a security boundary β don't share session state across untrusted processes.