Skip to main content

CDMObject (the union)

Source fileschemas/cdm_object.schema.json
$idhttps://synapsecommand.local/cdm/1.0.0/cdm_object.schema.json
CDM schema version1.0.0
SHA-256 of sourcef7d49f512d36b44923af0f20e75b1bbb2541cefd994a40788ae2365c32d6579e

The union

A mixed stream is validated against this schema without guessing: the object_kind discriminator names which of the four canonical shapes an object is, so a consumer validates one object at a time and never has to try all four.

Discriminator valueObject
entityEntity
eventEvent
plan_objectPlanObject
trackTrack

Referenced definitions

Every $ref on this page resolves to one of these, inlined here so the page is a complete reference and not a starting point for chasing pointers.

Affiliation

Maps to MIL-STD-2525 standard identity — see models.standard_identity().

Four members, not 2525's seven: PENDING, ASSUMED_FRIEND and SUSPECT are judgements a fusion layer makes, not facts an adapter can read off a wire format. An adapter that invented ASSUMED_FRIEND would be doing business logic, which adapters may not do. The source's own wording is preserved in attributes when it is finer than this.

Closed vocabulary — a value outside this list is invalid, and UNKNOWN is a

member rather than a null wherever the enum has one.

Value
FRIENDLY
HOSTILE
NEUTRAL
UNKNOWN

Entity

Anything that exists on the map, at a stated time, with stated confidence.

FieldTypeRequiredDescription
affiliationAffiliationyes
attributesobjectnoSource-specific fields the CDM has no home for. The never-drop bag: park data here rather than discarding it.
confidencenumber | nullno0..1. None = unknown; 0 means certainty-that-not, which is a claim. Default null. (≥ 0; ≤ 1)
entity_idstring (uuid)yesStable across updates — derived, see ids.derive(). Never drawn at random.
entity_typeEntityTypeyes
integrityIntegrity | nullnoPQC signature block — designed, not yet populated. Default null.
kinematicsKinematics | nullnoDefault null.
object_kind"entity"no
positionPosition | nullnoNone = position unknown. NEVER a Position holding zeros. Default null.
schema_versionstringnoSemver of the CDM this object was written against. Default "1.0.0".
sourceSourceRefyesWhich adapter produced this object. Required on every kind.
source_idsarray<SourceId>yesEvery external identifier this object is known by. At least one, on EVERY kind — see the class docstring. (min items 1)
symbolstring | nullnoMIL-STD-2525D SIDC, 20 digits. None when the source states no symbol — see symbology.sidc_from_affiliation() for deriving one. Default null.
valid_fromstringyesWhen this state began. (pattern ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$)
valid_tostring | nullnoWhen it ceased. None = still current / open-ended. Default null. (pattern ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

EntityType

Closed vocabulary — a value outside this list is invalid, and UNKNOWN is a

member rather than a null wherever the enum has one.

Value
UNIT
PLATFORM
SENSOR
FACILITY
EVACUEE_GROUP
INTERFERENCE_SOURCE
OVERLAY_OBJECT
UNKNOWN

Event

Anything that happens. The audit-bearing object: two timestamps and a source, always.

FieldTypeRequiredDescription
event_idstring (uuid)yes
event_typeEventTypeyes
geometryPoint | LineString | Polygon | nullnoGeoJSON, WGS84, [lon, lat] order — e.g. a jamming footprint. Default null.
integrityIntegrity | nullnoPQC signature block — designed, not yet populated. Default null.
object_kind"event"no
observed_atstringyesWhen the SOURCE saw it. Never receipt time. (pattern ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$)
payloadobjectnoEvent-specific fields. Validated against PAYLOAD_MODELS[event_type] when one is registered; free-form otherwise. Also the never-drop bag for events.
received_atstringyesWhen WE took delivery. Never source time. (pattern ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$)
related_entitiesarray<string (uuid)>noentity_id values this event concerns. Empty when the event concerns no specific entity (a feed-level status change).
schema_versionstringnoSemver of the CDM this object was written against. Default "1.0.0".
severitySeverityyes
sourceSourceRefyesWhich adapter produced this object. Required on every kind.
source_idsarray<SourceId>yesEvery external identifier this object is known by. At least one, on EVERY kind — see the class docstring. (min items 1)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

EventType

Closed vocabulary — a value outside this list is invalid, and UNKNOWN is a

member rather than a null wherever the enum has one.

Value
DETECTION
GNSS_INTERFERENCE
TRACK_UPDATE
ALERT
STATUS_CHANGE
PLAN_INJECT
SIM_RESULT

Integrity

DESIGNED, NOT IMPLEMENTED — the field the PQC signature will occupy.

No crypto happens in this package (tests/test_cdm_boundary.py asserts the package imports no crypto module). The field exists from day one so that turning signing on is a value change rather than a schema change: a schema change would be a MAJOR bump rippling through every store and every consumer, and would arrive exactly when the signing work is already late.

algorithm is a free string rather than an enum, naming what the platform's ledger already uses — ML-DSA-87 for entry signatures, SLH-DSA for checkpoints. Free, because the algorithm that replaces those is not knowable now, and an enum would make the migration a MAJOR bump for a value nobody reasons over programmatically.

All three fields or none. A block holding a signature with no algorithm is unverifiable, and an unverifiable signature that LOOKS present is worse than an absent one: it reads as assurance to everything downstream that does not check.

FieldTypeRequiredDescription
algorithmstringyese.g. ML-DSA-87, SLH-DSA-SHAKE-256s. (min length 1)
chain_hashstringyesHash binding this object to the chain. (min length 1)
signaturestringyes(min length 1)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

Kinematics

Motion. Every field optional, and absent means UNKNOWN, never zero.

This is the AIS sentinel lesson in schema form: 0 kt is measured stillness, 0 deg is a course due north, 0 m/s climb is level flight. All three are real measurements, so none of them can double as "no data" — the adapter translates the source's sentinel to None.

FieldTypeRequiredDescription
climb_mpsnumber | nullnoMetres per second, negative = descending. Default null.
course_degnumber | nullnoDegrees true, [0, 360). Default null. (≥ 0; < 360)
speed_mpsnumber | nullnoMetres per second. Default null. (≥ 0)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

LineString

FieldTypeRequiredDescription
coordinatesarray<array<number>>yes(min items 2)
type"LineString"no

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

ObjectType

What we push OUT — the egress direction, e.g. to TAK as a drawing object.

Closed vocabulary — a value outside this list is invalid, and UNKNOWN is a

member rather than a null wherever the enum has one.

Value
COA_SKETCH
ROUTE
CONTROL_MEASURE
ANNOTATION

PlanObject

What we push OUT: a drawing a commander's plan puts on someone else's map.

Geometry is REQUIRED here, unlike on Event. An overlay with no geometry cannot be drawn, so an egress adapter would have to either invent a location or silently drop the object — and a COA sketch that quietly fails to appear on the TAK client is the worst of the three outcomes, because everyone assumes it arrived.

FieldTypeRequiredDescription
expires_atstring | nullnoWhen the drawing should disappear. None = until explicitly removed — which for a stale COA sketch on a live map is a decision, so state it. Default null. (pattern ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$)
geometryPoint | LineString | PolygonyesGeoJSON, WGS84, [lon, lat] order. Required.
integrityIntegrity | nullnoPQC signature block — designed, not yet populated. Default null.
labelstring | nullnoWhat a client shows next to the drawing. None = unlabelled; never an empty string, which renders as a blank callout. Default null. (min length 1)
object_idstring (uuid)yes
object_kind"plan_object"no
object_typeObjectTypeyes
schema_versionstringnoSemver of the CDM this object was written against. Default "1.0.0".
sourceSourceRefyesWhich adapter produced this object. Required on every kind.
source_idsarray<SourceId>yesEvery external identifier this object is known by. At least one, on EVERY kind — see the class docstring. (min items 1)
styleobjectnoRendering HINTS, not requirements — stroke, fill, opacity, dash. A receiving client is free to ignore them, so nothing that changes MEANING may live here (an affiliation belongs on the entity, not in a colour).

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

Point

FieldTypeRequiredDescription
coordinatesarray<number>yes
type"Point"no

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

Polygon

FieldTypeRequiredDescription
coordinatesarray<array<array<number>>>yes(min items 1)
type"Polygon"no

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

Position

A fix. Both coordinates required — that is how the null-never-zero rule is structural.

An unknown position is the ABSENCE of this object (entity.position is None), never a Position holding zeros. Because lat and lon are required here, an adapter cannot express "unknown" as (0, 0) even by accident: it has to either omit the Position or state a real coordinate. Coordinate zero is a real point in the Gulf of Guinea, and a contact painted there is a contact that does not exist.

accuracy_m absent means unknown accuracy, NOT perfect accuracy. Zero would mean a fix with no error, which no sensor produces.

FieldTypeRequiredDescription
accuracy_mnumber | nullnoMetres, 1-sigma. None = unknown, never 0. Default null. (≥ 0)
alt_mnumber | nullnoMetres HAE. None = unknown. Default null.
latnumberyesWGS84 decimal degrees. (≥ -90; ≤ 90)
lonnumberyesWGS84 decimal degrees. (≥ -180; ≤ 180)
position_sourcePositionSourceyesHow the fix was obtained — the field that survives GNSS denial.

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

PositionSource

How the position was obtained. Load-bearing in a GNSS-denied environment.

This is the field that lets a commander tell a fix from a guess. When PNTMAP reports jamming over an area, every GNSS-sourced position inside that area becomes suspect and every INERTIAL or MANUAL one does not — a distinction that is impossible to make after the fact if the adapter flattened them all to "position".

Closed vocabulary — a value outside this list is invalid, and UNKNOWN is a

member rather than a null wherever the enum has one.

Value
GNSS
INERTIAL
MANUAL
ESTIMATED

Severity

Closed vocabulary — a value outside this list is invalid, and UNKNOWN is a

member rather than a null wherever the enum has one.

Value
INFO
ADVISORY
WARNING
CRITICAL

SourceId

One external identifier for an object — the provenance mapping.

A list of these, not one, because the same object arrives from several systems: the same vessel is an MMSI to AIS, a track number to STANAG 4676 and a UID to TAK. Fusion joins them later; the adapter's job is to record which name its own system used, and never to overwrite another system's entry.

FieldTypeRequiredDescription
external_idstringyesThat system's own identifier. (min length 1)
systemstringyesThe external system, e.g. PNTMAP, TAK. (min length 1)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

SourceRef

Which adapter produced this object, from which system, and whether it is real.

synthetic is required and has no default. Every fixture in this repository is synthetic and every scenario package is too (TR-12), and the platform keeps the synthetic and live layers apart over one interface — so an object that does not say which layer it belongs to cannot be filed. A default of false would silently promote exercise data to operational data, which is the dangerous direction; a default of true would silently demote live data and hide it from an operator. There is no safe default, so there is no default.

FieldTypeRequiredDescription
adapterstringyesAdapter name, e.g. pntmap. (min length 1)
adapter_versionstringyesAdapter semver. (min length 1)
syntheticbooleanyestrue for anything not from a real source (TR-12).
systemstringyesThe external system this came from. (min length 1)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

Track

An entity's position history, in time order. The order is a contract, not a hope.

A scrambled sample list produces nonsense the moment anything differentiates it — speed from consecutive positions, a heading arrow, a predicted point. So non-decreasing timestamps are validated here, at the boundary, where the defect is one adapter's bug rather than a mystery in a fusion layer three hops downstream.

Equal timestamps are ALLOWED: two sensors reporting the same instant is real, and rejecting it would refuse legitimate multi-source data.

FieldTypeRequiredDescription
entity_idstring (uuid)yesThe Entity this history belongs to.
integrityIntegrity | nullnoPQC signature block — designed, not yet populated. Default null.
object_kind"track"no
samplesarray<TrackSample>yesTime-ordered, non-decreasing. At least one. (min items 1)
schema_versionstringnoSemver of the CDM this object was written against. Default "1.0.0".
sourceSourceRefyesWhich adapter produced this object. Required on every kind.
source_idsarray<SourceId>yesEvery external identifier this object is known by. At least one, on EVERY kind — see the class docstring. (min items 1)
track_idstring (uuid)yes
track_qualitynumber | nullno0..1. None = not assessed, never 0. Default null. (≥ 0; ≤ 1)

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).

TrackSample

One position at one instant. The unit STANAG 4676 calls a track point.

FieldTypeRequiredDescription
observed_atstringyesRFC 3339 UTC, exactly three decimal places, always Z. (pattern ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$)
positionPositionyes

additionalProperties: false — unknown keys are rejected. Source-specific fields belong in the declared extension bags (Entity.attributes, Event.payload).