Entity
| Source file | schemas/entity.schema.json |
$id | https://synapsecommand.local/cdm/1.0.0/entity.schema.json |
| CDM schema version | 1.0.0 |
| SHA-256 of source | 55d02434c12074c145da2364eb0be9a31a7d85625bcfa1dcb974b428af4d2299 |
Anything that exists on the map, at a stated time, with stated confidence.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
affiliation | Affiliation | yes | |
attributes | object | no | Source-specific fields the CDM has no home for. The never-drop bag: park data here rather than discarding it. |
confidence | number | null | no | 0..1. None = unknown; 0 means certainty-that-not, which is a claim. Default null. (≥ 0; ≤ 1) |
entity_id | string (uuid) | yes | Stable across updates — derived, see ids.derive(). Never drawn at random. |
entity_type | EntityType | yes | |
integrity | Integrity | null | no | PQC signature block — designed, not yet populated. Default null. |
kinematics | Kinematics | null | no | Default null. |
object_kind | "entity" | no | |
position | Position | null | no | None = position unknown. NEVER a Position holding zeros. Default null. |
schema_version | string | no | Semver of the CDM this object was written against. Default "1.0.0". |
source | SourceRef | yes | Which adapter produced this object. Required on every kind. |
source_ids | array<SourceId> | yes | Every external identifier this object is known by. At least one, on EVERY kind — see the class docstring. (min items 1) |
symbol | string | null | no | MIL-STD-2525D SIDC, 20 digits. None when the source states no symbol — see symbology.sidc_from_affiliation() for deriving one. Default null. |
valid_from | string | yes | When 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_to | string | null | no | When 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$) |
:::info additionalProperties: false
Unknown keys are rejected. That is safe only because the CDM pairs strictness
with a declared escape hatch — Entity.attributes and Event.payload accept
anything — so an adapter never has to choose between dropping a field and failing
validation.
:::
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 |
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 |
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.
| Field | Type | Required | Description |
|---|---|---|---|
algorithm | string | yes | e.g. ML-DSA-87, SLH-DSA-SHAKE-256s. (min length 1) |
chain_hash | string | yes | Hash binding this object to the chain. (min length 1) |
signature | string | yes | (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.
| Field | Type | Required | Description |
|---|---|---|---|
climb_mps | number | null | no | Metres per second, negative = descending. Default null. |
course_deg | number | null | no | Degrees true, [0, 360). Default null. (≥ 0; < 360) |
speed_mps | number | null | no | Metres per second. Default null. (≥ 0) |
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.
| Field | Type | Required | Description |
|---|---|---|---|
accuracy_m | number | null | no | Metres, 1-sigma. None = unknown, never 0. Default null. (≥ 0) |
alt_m | number | null | no | Metres HAE. None = unknown. Default null. |
lat | number | yes | WGS84 decimal degrees. (≥ -90; ≤ 90) |
lon | number | yes | WGS84 decimal degrees. (≥ -180; ≤ 180) |
position_source | PositionSource | yes | How 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 |
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.
| Field | Type | Required | Description |
|---|---|---|---|
external_id | string | yes | That system's own identifier. (min length 1) |
system | string | yes | The 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.
| Field | Type | Required | Description |
|---|---|---|---|
adapter | string | yes | Adapter name, e.g. pntmap. (min length 1) |
adapter_version | string | yes | Adapter semver. (min length 1) |
synthetic | boolean | yes | true for anything not from a real source (TR-12). |
system | string | yes | The 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).