| Crates.io | cot_publisher |
| lib.rs | cot_publisher |
| version | 2.0.0 |
| created_at | 2023-05-29 08:15:59.214498+00 |
| updated_at | 2025-11-04 14:34:41.000924+00 |
| description | Cursor On Target (COT) Publisher for multicast UDP or TCP for ATAK ecosystem |
| homepage | https://github.com/martynp/cot_publisher |
| repository | https://github.com/martynp/cot_publisher |
| max_upload_size | |
| id | 877000 |
| size | 143,319 |
A Rust library for publishing Cursor on Target (COT) messages to multicast networks and TAK Servers.
This library provides both async and blocking interfaces for integrating COT functionality into Rust applications
The crate supports publishing COT messages through two transport mechanisms:
Multicast Publishing
TLS/mTLS Publishing (TAK Server etc.)
Async and Blocking API's
Add this to your Cargo.toml:
[dependencies]
cot_publisher = "2.0.0-rc2"
For blocking operations, enable the blocking feature:
[dependencies]
cot_publisher = { version = "2.0.0-rc2", features = ["blocking"] }
use cot_publisher::CotPublisher;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create publisher for standard TAK multicast address
let publisher = CotPublisher::new_multicast("239.2.3.1".parse()?, 6969);
// Create a COT message with unique identifier and type
let mut cot = publisher.create_cot("my-device-001", "a-f-G-E-V-C")?;
// Set position and contact information
cot.set_position(51.5074, -0.1278); // London coordinates
cot.set_contact(Some("CALLSIGN-1"), Some("192.168.1.100:8080"));
// Publish the message
cot.publish().await?;
Ok(())
}
use cot_publisher::blocking::CotPublisher;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let publisher = CotPublisher::new_multicast("239.2.3.1".parse()?, 6969);
let mut cot = publisher.create_cot("my-device-001", "a-f-G-E-V-C")?;
cot.set_position(51.5074, -0.1278);
cot.set_contact(Some("CALLSIGN-1"), None);
cot.blocking_publish()?;
Ok(())
}
use cot_publisher::{CotPublisher, Credentials, Source, TakServerSetting};
use url::Url;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load client certificates
let credentials = Credentials::from_unencrypted_pem(
Source::File("/path/to/client-cert.pem".to_string()),
Source::File("/path/to/client-key.pem".to_string()),
Some(Source::File("/path/to/ca-cert.pem".to_string())),
)?;
// Configure TLS settings
let settings = TakServerSetting {
tls: true,
client_credentials: Some(credentials),
ignore_invalid: false,
verify_hostname: true,
auto_reconnect: true,
};
// Connect to TAK Server
let tak_server_url = Url::parse("https://takserver.example.com:8089")?;
let mut publisher = CotPublisher::new_takserver(tak_server_url, settings);
// Wait for connection establishment
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
// Create and publish COT message
let mut cot = publisher.create_cot("takserver-client-001", "a-f-G-E-V-C")?;
cot.set_position(40.7128, -74.0060); // New York coordinates
cot.set_contact(Some("NYC-BASE"), None);
cot.publish().await?;
Ok(())
}
The COT type field follows the standard COT taxonomy. Common types include:
a-f-G-E-V-C: Ground friendly unit, equipment, vehicle, combata-f-G-E-V-M: Ground friendly unit, equipment, vehicle, medicala-f-G-I: Ground friendly unit, installationsa-h-G: Ground hostile unita-n-G: Ground neutral unita-u-G: Ground unknown unit// Set position with altitude and accuracy information
cot.set_position_extended(
51.5074, // latitude
-0.1278, // longitude
100.0, // altitude (HAE - Height Above Ellipsoid)
10.0, // circular error (CE)
15.0 // linear error (LE)
);
// Add precision location source information
cot.set_precision_location(Some("GPS"), Some("GPS"));
COT messages support custom XML detail sections for application-specific data:
cot.set_xml_detail(Some(r#"
<custom>
<sensor type="thermal" status="active"/>
<battery level="85" voltage="12.4"/>
</custom>
"#));
When connecting to TAK Server, you need client certificates. TAK Server typically generates certificates protected with the password "atakatak".
It is possible to provide the password as part of the connection Credential or remove password protection using:
openssl rsa -in client.key -out client-nopass.key
Certificates can be loaded from files or embedded as strings in your application:
// From files
let credentials = Credentials::from_unencrypted_pem(
Source::File("client-cert.pem".to_string()),
Source::File("client-key.pem".to_string()),
Some(Source::File("/path/to/ca-cert.pem".to_string())),
)?;
// From strings (useful for embedded certificates)
let cert_pem = include_str!("../certs/client-cert.pem");
let key_pem = include_str!("../certs/client-key.pem");
let credentials = Credentials::from_unencrypted_pem(
Source::String(cert_pem.to_string()),
Source::String(key_pem.to_string()),
Source::String(ca_cert.to_string()),
)?;
If the CA Certificate is not provided in the ClientCredentials then the system native certificates will be used.
To accept COT messages, configure TAK Server with a streaming data feed (in many cases a mTLS connection is enabled by default):
Enable error logging by adding the emit_errors feature:
[dependencies]
cot_publisher = { version = "2.0.0-rc1", features = ["emit_errors"] }
When enabled logs are emitted via the log crate.
The repository includes several complete examples:
simple_multicast.rs: Basic multicast publishing with position updatesblocking_multicast.rs: Blocking interface demonstrationtakserver_connection.rs: TAK Server connection with TLS certificatesRun examples with:
cargo run --example simple_multicast
cargo run --features blocking --example blocking_multicast
The takserver example requires certificate / key information to be added before it can be used.
This project is licensed under the MIT License.