TAK Bench is a Rust tool for explicitly authorized testing of TAK/CoT servers that accept CoT XML over raw TCP, TLS, or mTLS. Its stream framing follows the official TAK Server StreamingCotProtocol: fragmented and concatenated CoT events, as well as authentication preambles, are supported.
Use this tool only against servers you administer or are authorized to test.
Commands that open connections require --acknowledge-authorization or authorization.acknowledged: true. The destination must be listed in allow_hosts, except for loopback in local. Production additionally requires --environment production --allow-production, accepts only smoke, and remains limited to three clients, 15 minutes, and one position every 30 seconds or less frequently. Controlled staging load profiles may use 100, 500, or 1000 clients; they require --allow-staging-stress, authorization, an allowlisted target, and explicit run.max_clients. See controlled load tiers.
Invalid events are blocked by default. In staging they require --allow-invalid-events; in local and temporary environments they still require max_events and a maximum rate of one event per second.
Validate a configuration without opening a connection:
cargo run -- validate --config examples/functional.yamlRun an explicitly authorized local TCP smoke test:
cargo run -- smoke \
--server 127.0.0.1:8089 \
--acknowledge-authorization \
--duration 2mA YAML configuration can define TLS/mTLS, participant roles, ramp-up, timeouts, reconnect, readiness synchronization, routing observations, fragmentation, thresholds, and stable JSON output. CLI flags override equivalent fields. --lifecycle-jsonl (or output.lifecycle_jsonl: true) reserves stdout for sanitized orchestration events; the JSON report remains the primary artifact. Unsupported scenario and scheduling options are rejected before dialing. Start with functional-routing.yaml.
External orchestrators should consume lifecycle JSON Lines as an ephemeral control stream rather than upload them as an artifact. Persist only output.json, validate routing by the individual sender, receiver, and expectation fields, and configure the read timeout longer than the overall functional run so the global scenario deadline owns silent-receiver completion.
- Fixed-position CoT events with a UID and per-event correlation ID.
- TCP, TLS, and mTLS with hostname verification always enabled, including optional
per-participant certificate/key path templates using
{participant_id}. - Optional TAK enrollment and ATAK data-package credentials. Enrollment uses only
/Marti/api/tls/configand/Marti/api/tls/signClient/v2with Basic Auth; it never provisions users or groups, revokes certificates, or calls administrative APIs. - Authenticated ATAK packages are imported in memory. PKCS#12 keychains and truststores use pure
Rust parsing; trust-only packages require stdin CSV enrollment. Packages in one run must target
the configured
target.server. - Concurrent reading and writing, received/duplicate message counts, and local delivery latency when the correlation extension is preserved.
immediate,linear,step, andrandomizedramps; connection, message, latency, and drop thresholds that cooperatively stop a run.- Participant roles (
send_only,receive_only, andsend_receive), bounded reconnect with jitter, per-operation timeouts, CoT batching and fragmentation. - Observational routing assertions: each expected or forbidden sender/receiver pair has its own sanitized result and observed count; the harness does not configure server routing.
- An optional readiness barrier gates sender workloads on named participants. "Ready" means that the participant completed TCP and, when configured, TLS/mTLS setup and can execute its local role. It does not claim server-side authorization, registration, presence, or policy acceptance.
- Optional sanitized JSON Lines lifecycle events (
participant_connected,participant_ready,participant_disconnected, andrun_completed) containing aliases and classified reasons only. - Terminal and JSON reports with final status, abort reason, sanitized configuration, metrics, per-participant classified failures, and assertion results.
- Credential reports distinguish preparation from workload time using
credential_summary,credential_failures, andworkload_elapsed_ms. Preparation failures still emit a sanitized failed report andrun_completedlifecycle event. - A server-neutral
Provisionerinterface andFakeProvisionerfor tests; no Vanguarda-specific or other server-specific API is embedded. - A reusable
tak_bench::runnermodule so external integrations can provision their own fixtures and execute the same guarded workload lifecycle as the CLI from one public crate.
The harness only observes whether a server accepts TCP/TLS/mTLS connections, delivers events to clients, preserves the correlation identifier required by configured assertions, and closes or rejects sockets according to its own policy. It has no administrative view into a TAK Server.
Preservation of the correlation extension and acceptance of a receive-only client that sends no initial announcement are integration properties of the consumer's chosen server. tak_bench does not assume either behavior. Provisioning identities, certificates, groups, policy, revocation, and cleanup remains the external orchestrator's responsibility.
The default remains neutral and uses the existing PEM fields under tls. Credential acquisition is
enabled only when credentials.source is present. The CSV is never read from a file or argument:
library callers pass a zeroizing buffer and the CLI reads it exclusively from stdin.
credentials:
assignment: round_robin
source:
kind: tak_enrollment
users: stdin_csv
endpoint:
host: tak.example
port: 8446
sni: tak.example
ca: /run/secrets/enrollment-ca.pem
rsa_bits: 4096
max_parallel: 4
timeout: 30sThe CSV header must be exactly participant,username,password. participant may be empty. A
supplied alias receives that identity first; remaining participants reuse identities in deterministic
round-robin order. Each row is enrolled once.
printf 'participant,username,password\nclient-0,alice,secret\n' \
| tak-bench run --config staging/enrollment.yaml --acknowledge-authorizationATAK packages use kind: atak_data_packages and a directory. Authenticated packages provide a
client PKCS#12 and truststore directly. Trust-only packages require an enroll_trust_only block
with users: stdin_csv and an enrollment endpoint. Packages must contain an unambiguous manifest
and PKCS#12 member, have no unsafe ZIP paths or encryption, and all declare the same target.
Keys, certificates, passwords, CSV, manifest contents, and package paths are excluded from stdout, stderr, lifecycle events, and JSON reports.
The repository includes a neutral composite action. Pin it to an approved full commit SHA; it installs the supported Rust toolchain and builds that exact source revision with --locked. With a configuration it runs the workload and returns the sanitized report path, but it never uploads artifacts, provisions fixtures, revokes credentials, or calls a server administration API:
- name: Run neutral routing check
id: tak-bench
uses: valmojr/tak_bench@<full-commit-sha>
with:
config: staging/routing.yaml
report: artifacts/routing-report.json
lifecycle-jsonl: "true"
allow-staging-stress: "true" # only for an authorized, allowlisted staging target
- name: Upload sanitized evidence only
uses: actions/upload-artifact@v7
with:
name: tak-bench-report
path: ${{ steps.tak-bench.outputs.report-path }}
if-no-files-found: errorWhen a consumer-owned wrapper must react to lifecycle events while the process is running, use setup-only mode and pass the binary output to that wrapper. The wrapper remains responsible for fixture lifecycle and any product-specific action:
- name: Build approved TAK Bench revision
id: tak-bench
uses: valmojr/tak_bench@<full-commit-sha>
- name: Run consumer staging wrapper
env:
TAK_BENCH_BIN: ${{ steps.tak-bench.outputs.binary-path }}
run: ./scripts/run-staging-routing.shFor enrollment or trust-only packages, use setup-only mode and have the consumer-owned wrapper provide the CSV through the binary's stdin. The action does not accept credential CSV as a regular input, write it to a temporary file, or place it in process arguments.
Lifecycle JSONL should be consumed live and discarded. Only the sanitized JSON report should be uploaded as evidence.
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo build --release --locked
cargo package --lockedSlow local readers, bounded abrupt disconnects, bounded slow first writes, and carefully rate-limited invalid inputs are opt-in scenario controls. They are never production-safe. See scenario guidance.
Before a release tag, run an authorized mTLS preflight and smoke workload against the intended TAK Server version using examples/smoke-mtls.yaml. Loopback fixtures validate transport behavior but do not claim compatibility with every server deployment. See the GitHub Actions and release guide for the tag and artifact process.
Licensed under either the Apache License 2.0 or MIT license, at your option.