Cursor on Target is the smallest schema that does the most work in modern tactical software. A single XML document — a few hundred bytes — carries a position, an identity, a confidence, and a lifetime, and that document is enough to put a marker on every map in a mesh network. It originated as a MITRE effort to let an operator pass a target from one system to another with a literal cursor gesture, and it became the lingua franca of the TAK ecosystem. This is a field-by-field walkthrough of the Cursor on Target format: the event model, the type taxonomy, the point geometry, the detail container, and the realities of generating and validating it correctly.
1. the CoT event model
Every CoT message is a single <event> element. There is no envelope, no header, no separate message-type registry — the root element is the message. This is the deliberate design choice that makes CoT cheap: one recursive schema carries a friendly soldier, a hostile vehicle, a geofence, a chat message, an emergency beacon, and a sensor track, with no per-message-type code path on the wire.
The event holds exactly one <point> child (where the thing is) and an optional <detail> child (everything else about it). The semantics of an event are fully determined by its attributes plus the open content of <detail>. A parser that understands the four core attributes and the point can render any CoT message on a map without understanding a single detail extension — that graceful-degradation property is why CoT scales across vendors who never coordinated.
It is worth dwelling on what is not in the model. There is no schema-level distinction between a unit, a marker, a chat message, and a sensor track — those are all events, differentiated only by their type and the detail children they carry. There is no acknowledgement, no sequence number, and no session. CoT is fire-and-forget; reliability, ordering, and delivery are pushed down to the transport (TCP, TLS, or multicast UDP) and the application above. That minimalism is the schema's defining property: it does one thing — say where something is and what it is, for a bounded time — and refuses to grow a protocol stack of its own.
2. event attributes
The <event> element carries a fixed set of attributes. version is the schema version, almost always 2.0. uid is the globally unique identifier for the thing being described — not the message, the thing. Re-sending an updated position for the same entity reuses the same uid; that is how a receiver knows to move an existing marker rather than spawn a new one. UIDs are free-form strings, but TAK clients conventionally use a stable per-device identifier (e.g. ANDROID-serial) for self-reports.
type is the MITRE type string (Section 3). how describes the provenance of the data — how the position was derived. Common values are h-g-i-g-o (human, GPS-derived, manually entered) and m-g (machine, GPS). The how field is what lets a fusion engine weight a hand-jammed spot report differently from a live GPS feed.
The lifecycle triad is time, start, and stale, all ISO 8601 UTC timestamps. time is when the message was produced. start is when the information becomes valid. stale is when it expires. The window between start and stale is the event's validity lifetime: after stale passes, a conforming receiver must treat the event as expired and remove or grey out the marker. A self-position report from a moving unit might set stale to time + 75 seconds; a static survey point might set it hours out. Getting this triad right is the single most common source of "ghost markers" — set stale too far in the future and dead tracks linger; set it too short and live tracks flicker.
Key insight: CoT has no explicit delete message. You retire a track by letting it go stale, or by sending one final update whose stale time is already in the past. State management in a CoT network is therefore a timeout problem, not a transaction problem — every receiver is independently garbage-collecting on its own clock, which is why clock skew between nodes is an operational hazard, not a cosmetic one.
3. the MITRE type hierarchy
The type attribute encodes what the thing is using MITRE's dotted, hyphenated taxonomy. The most important family is the atom type, prefixed a-. An atom type reads as a sequence of hyphen-separated tokens: a-f-G-U-C.
The first token after a is affiliation: f friend, h hostile, n neutral, u unknown, p pending, plus assumed/suspect variants. The next token is battle dimension: G ground, A air, S surface (sea), U subsurface, P space, F SOF. The remaining tokens drill down the MIL-STD-2525 symbol hierarchy — a-f-G-U-C is a friendly ground unit, combat, and so on. A wildcard a-f-G-* means "a friendly ground thing, unspecified beyond that," and renderers fall back to the nearest defined symbol. Non-atom families use different prefixes: b- for bits (sensor/geometry data, e.g. b-m-p-s-p-i for a sensor point of interest), t- for tasking, and y- for replies. The genius of the dotted taxonomy is that it is prefix-decodable: a client that only knows a-f-G can still place a generic friendly-ground icon and degrade gracefully on the tail it does not recognise.
4. the point element
The <point> element is mandatory and carries five attributes, all required. lat and lon are WGS-84 decimal degrees. hae is height above ellipsoid in metres — note "above ellipsoid," not above mean sea level; mixing HAE and orthometric height (the geoid offset can exceed 30 m) is a classic vertical-error bug when CoT meets a system that expects MSL.
ce is the circular error — the horizontal 1-sigma uncertainty radius in metres. le is the linear error — the vertical uncertainty in metres. Together they let a receiver draw an accuracy ring rather than a deceptively precise pinpoint. The sentinel value 9999999 (often written 9999999.0) means "unknown" — it is not a real measurement, it is the schema's null. A manually-dropped point with no surveyed accuracy carries ce="9999999" le="9999999", and fusion logic must special-case that value rather than treat it as a 10-thousand-kilometre error.
Because every attribute is required, there is no such thing as a point without a height or without an error estimate — the schema forces the producer to make a claim, even if that claim is "unknown." This is a quietly good design decision: a receiver never has to guess whether a missing field means zero, unknown, or default. It either has a real number or it has the sentinel, and the two are unambiguous. The cost is that lazy encoders hard-code hae="0.0" for everything, which is worse than the sentinel because it looks like a real sea-level measurement. If you do not know the height, say so with 9999999; do not assert zero.
5. the detail element
The <detail> element is the open extension container, and it is where CoT's ecosystem actually lives. The schema places no constraint on its children — any well-formed XML is legal — which is what allowed TAK to layer a rich application protocol on top of a generic SA format without forking it.
The conventional sub-elements are widely honoured. <contact> carries a human-readable callsign and, for TAK, endpoint addressing for direct messaging. <track> carries course and speed for moving entities, turning a static point into a vector. <remarks> is free text. <status> reports things like battery level. <__group> (double underscore) assigns the unit to a TAK team colour and role — Cyan, Team Member — driving the colour of the icon on every teammate's screen. <takv> reports the TAK client version, device, and platform. Because detail is open, a receiver simply skips the children it does not recognise, which is the entire basis of CoT and TAK interoperability across heterogeneous clients.
6. CoT vs Link 16 and VMF
CoT occupies the same conceptual space as the J-series and VMF, but with opposite design priorities. Link 16 J-messages are fixed-format bit-packed words sized to a TDMA slot; VMF (MIL-STD-6017) is a variable but tightly bit-oriented format for low-bandwidth bearers. CoT is verbose XML built for IP networks where bytes are cheap and developer time is not.
Mapping CoT to a J-message is lossy in both directions. CoT's affiliation and battle dimension map cleanly onto J3.x track fields and onto VMF identity fields, and lat/lon/hae translate directly. The impedance mismatch lives in precision and semantics: a J-series track quality is a discrete enumerated value, where CoT's ce/le are continuous metres; CoT's open <detail> has no fixed-format counterpart and gets dropped wholesale at a gateway. Conversely, J-series fields like specific IFF modes or PPLI-derived network participation have no native CoT slot and must be smuggled into custom detail extensions. Gateways that bridge CoT and tactical data links therefore carry an opinionated, hand-maintained field map — the same translator-with-opinions problem that shows up everywhere in NATO C2.
7. streaming and TAK
In production, CoT is a stream, not a document. TAK Server multiplexes CoT events between clients: a unit's self-position report flows up over a persistent TCP (often TLS) connection at a configurable rate — commonly every 1 to 10 seconds depending on movement and the "dynamic reporting" setting — and the server fans it out to subscribers, optionally filtered by mission, group, or geofence. Mesh SA, the serverless mode, multicasts the same events over UDP on the local network so a squad operates with no infrastructure.
Message rates drive the engineering. A 200-node exercise with everyone reporting every 2 seconds is 100 events/second of self-position alone, before sensor tracks. The server does not maintain a deletion protocol for any of it; instead, every client independently garbage-collects on the stale timestamps it has seen. Stale-driven garbage collection is elegant — no leader, no consensus — but it means a client that loses connectivity will watch its whole picture age out and vanish on schedule, which is usually the correct behaviour and occasionally a nasty surprise during a comms blackout.
8. validating and generating CoT
The wire format is forgiving, which makes it easy to emit subtly broken CoT. Validate against the published Event.xsd schema during development, but know its limits: the XSD checks that point exists and that the lifecycle attributes are present and well-typed, but it cannot tell you that your stale is before your start, that your type token is meaningless, or that your hae is geoid height masquerading as ellipsoid height.
The recurring malformed-message bugs are predictable. Timestamps without the trailing Z or with local-zone offsets — CoT time is UTC, full stop, and a missing Z sends markers into the past or future. Inverted lifetimes where stale precedes start, producing events that are dead on arrival. Reusing a single uid for distinct entities, which makes two real tracks collapse into one flickering marker. Emitting a real-looking ce instead of the 9999999 sentinel for an unsurveyed point, which fools fusion into trusting garbage. When you build a CoT encoder, make the lifecycle triad a first-class type that enforces start ≤ stale and renders UTC with the Z, generate UIDs from a stable entity key rather than a per-message counter, and emit the unknown sentinel explicitly. Get those four invariants right and your CoT will interoperate with clients you have never seen — which is the whole point of the format.