Skip to content

Technical Telemetry Schema

Rylan Meilutis edited this page Jun 29, 2026 · 6 revisions

Telemetry Schema

As of v4.0.0, user telemetry schema is runtime state. DataEndpoint and DataType are stable numeric IDs on the wire, but the library no longer generates application-specific Rust enum variants or binding constants at compile time.

The default registry contains only built-in internal entries:

  • telemetry error endpoint/type
  • reliable-control packet types
  • discovery endpoint/types
  • time-sync endpoint/types when the timesync feature is enabled

Applications add user endpoints and data types at runtime through APIs, JSON seeding, or discovery schema sync.

Runtime IDs and Names

DataEndpoint(pub u32) and DataType(pub u32) are transparent runtime IDs. Use names for readability and only use raw IDs where you are assigning a wire ID intentionally.

let radio = DataEndpoint::named("RADIO");
let gps = DataType::named("GPS_DATA");

let maybe_radio = DataEndpoint::try_named("RADIO");
let maybe_gps = DataType::try_named("GPS_DATA");

Definitions carry:

  • numeric ID
  • string name
  • human-readable description
  • endpoint link-local flag
  • data type shape, allowed endpoints, reliability mode, queue priority, and E2E cryptography policy

Lookup/export APIs include:

  • endpoint_definition(...)
  • endpoint_definition_by_name(...)
  • data_type_definition(...)
  • data_type_definition_by_name(...)
  • known_endpoints()
  • known_data_types()
  • export_schema()

Registering Schema

Rust:

let radio = register_endpoint_id_with_description(
    DataEndpoint(100),
    "RADIO",
    "Downlink radio",
    false,
)?;

register_data_type_id_with_description(
    DataType(100),
    "GPS_DATA",
    "Latitude, longitude, altitude",
    MessageElement::Static(3, MessageDataType::Float32, MessageClass::Data),
    &[radio],
    ReliableMode::Ordered,
    80,
)?;

Use register_data_type_id_with_description_and_e2e_encryption(...) when a type should prefer or require E2E encrypted payloads.

Direct registration rejects conflicts:

  • same endpoint ID/name with different endpoint metadata
  • same data type ID/name with a different shape, endpoint set, reliability mode, priority, or E2E policy
  • data types referencing endpoints that do not exist

Endpoint handler registration also creates missing endpoints in std builds:

EndpointHandler::new_packet_handler(DataEndpoint(250), |_pkt| Ok(()));

If that endpoint ID did not exist, it is registered as ENDPOINT_250 and can be advertised through schema discovery.

Removing Schema

User endpoints and data types can be removed at runtime:

  • remove_endpoint(...)
  • remove_endpoint_by_name(...)
  • remove_data_type(...)
  • remove_data_type_by_name(...)

Removing an endpoint also removes user data types that reference it. Built-in internal entries cannot be removed.

JSON Runtime Seeding

JSON is optional runtime input. It is not used to generate user schema constants.

Host/std builds can seed from:

  • SEDSNET_STATIC_SCHEMA_PATH
  • SEDSNET_STATIC_IPC_SCHEMA_PATH
  • Rust register_schema_json_path(...) / register_schema_json_bytes(...)
  • C seds_schema_register_json_file(...) / seds_schema_register_json_bytes(...)
  • Python register_schema_json_file(...) / register_schema_json_bytes(...)

Embedded builds include telemetry_config.json bytes only if an application provides that file locally before building, then decode those bytes through the same runtime parser. The crate remains buildable/publishable without an application JSON file.

JSON shape:

{
  "endpoints": [
    {
      "rust": "Radio",
      "name": "RADIO",
      "description": "Downlink radio"
    }
  ],
  "types": [
    {
      "rust": "GpsData",
      "name": "GPS_DATA",
      "description": "GPS data",
      "reliable_mode": "Ordered",
      "priority": 80,
      "class": "Data",
      "element": { "kind": "Static", "data_type": "Float32", "count": 3 },
      "endpoints": ["Radio"]
    }
  ]
}

Notes:

  • description is preferred.
  • Legacy doc is still accepted as an alias.
  • Legacy broadcast_mode = "Never" is accepted and maps to link-local behavior.
  • IPC JSON loaded through the IPC seed path is applied as link-local overlay data.

Network Schema Sync

Discovery includes schema advertisements. When nodes connect, they can exchange the current endpoint/type list and merge compatible definitions.

Merge behavior:

  • new endpoint/type definitions are added
  • equivalent definitions are kept
  • ID/name conflicts are resolved deterministically so nodes converge on the same winner
  • data types with missing endpoint dependencies are skipped until those endpoints are known

Direct local registration remains stricter than network merge. If local code tries to register an existing data type with a different shape, registration returns BadArg.

Memory Budgeting

Runtime schema memory counts against the shared router/relay MAX_QUEUE_BUDGET. That same budget also covers RX/TX queues, reliable replay/out-of-order state, recent packet ID caches, and discovery topology.

If a received schema snapshot would exceed the budget, the merge is rejected and the current registry is left unchanged.

Payload Layouts

Schema shape still controls packet validation:

  • Static + numeric/bool: payload size must equal count * element_width.
  • Dynamic + numeric/bool: payload size must be a multiple of element width.
  • String: dynamic UTF-8 bytes; trailing NULs are ignored for validation.
  • Binary: raw bytes.
  • NoData: zero-length payload.

For static String or Binary payloads, the configured limits are:

  • runtime static_string_length, defaulting to the packaged STATIC_STRING_LENGTH
  • runtime static_hex_length, defaulting to the packaged STATIC_HEX_LENGTH

Compatibility Checklist

For v4 deployments:

  • Prefer named runtime lookup in application code: DataEndpoint::named(...) and DataType::named(...).
  • Seed required endpoints/types at router startup if they must always exist.
  • Let discovery sync optional or peer-defined schema over time.
  • Treat data type shape changes as incompatible unless you intentionally create a new type ID/name.
  • Budget for schema memory if you expect many dynamic endpoints/types.

Clone this wiki locally