STANAG 4817 / AEP-105 · 0.3.0-rc4 (SD-3 RC4)
Concepts

Protobuf

The Protobuf (Volume III) binary encoding of the HIBW model, and how it maps from the IDL.

BLUF

Protobuf is the compact binary encoding of the HIBW model (Volume III). In 0.3.0-rc4 the model is distributed as OMG IDL (the source of truth for binary encodings) plus JSON Schema and XSD; the standalone .proto bundle that shipped with 0.1.0 / 0.2.0 is not part of RC4. So the API generates .proto from the IDL for every draft, compiles it, and serves the result — the samples below are faithful proto3 derived from the RC4 model.

You don't have to run the transpiler yourself. The validation API ships the generated bundles and a JSON ⟷ protobuf codec:

  • Download a draft's full .proto tree (plus its compiled FileDescriptorSet and union map) from GET /protobuf/{version}/bundle.zip; list what's available at GET /protobuf.
  • Encode a JSON message to its protobuf binary with POST /encode (or the per-type POST /messages/{slug}/encode).
  • Decode a protobuf payload back to JSON and re-validate it with POST /decode (or POST /messages/{slug}/decode). Protobuf carries no type tag, so the generic /decode takes a ?message_type= query.

The codec translates the STANAG $discriminator on a union to/from the proto oneof automatically, so a JSON → protobuf → JSON round-trip is lossless.

The CATL message set changed between 0.2.0 and 0.3.0 (e.g. 0.2.0 had SET_WORLD_MODEL / ADD_NODE / STATUS; 0.3.0 has NODE_DESCRIPTION / NODE_STATUS / TASK_FEEDBACK / TASK_RESULT). Do not reuse the 0.2.0 .proto files as-is for RC4 — regenerate from the 0.3.0-rc4 IDL.

How Protobuf relates to the model

One logical model, several wire formats (see encodings). The IDL is the canonical definition; JSON Schema, XSD, and Protobuf are projections of it. Mapping conventions from IDL to proto3:

IDL constructproto3
structmessage
enumenum (with a *_UNSPECIFIED = 0 zero value)
union (discriminated)message with a oneof
sequence<T>repeated T
typedef scalar (GUID, Time)string
optional [0..1] fieldoptional scalar / message presence

The envelope

syntax = "proto3";
package catl.hibw.messages.core;

// 0.3.0-rc4 message set. proto3 requires a zero default, so a synthetic
// UNSPECIFIED is added ahead of the model's seven types.
enum MessageTypeEnum {
  MessageTypeEnum_UNSPECIFIED      = 0;
  MessageTypeEnum_TASK_ADMIN       = 1;
  MessageTypeEnum_NODE_STATUS      = 2;
  MessageTypeEnum_DYNAMIC_UPDATE   = 3;
  MessageTypeEnum_CHAT             = 4;
  MessageTypeEnum_TASK_FEEDBACK    = 5;
  MessageTypeEnum_NODE_DESCRIPTION = 6;
  MessageTypeEnum_TASK_RESULT      = 7;
}

message MessageHeader {
  MessageTypeEnum message_type = 1;
  string          source       = 2;  // GUID of the originating node
  string          version      = 3;  // schema version, e.g. "0.3.0"
  string          time_sent    = 4;  // ISO-8601 timestamp
}

A data element: Track

Track inherits the BaseWhatWhereWhen fields (flattened here) and adds a lifecycle phase — see base::track::Track.

syntax = "proto3";
package catl.hibw.base.track;

enum TrackPhase {
  TrackPhase_UNSPECIFIED   = 0;
  TrackPhase_DEAD_RECKONED = 1;
  TrackPhase_LOST          = 2;
  TrackPhase_TRACKED       = 3;
  TrackPhase_INACTIVE      = 4;
}

message Track {
  string            identifier               = 1;  // GUID, required
  optional string   source_of_information    = 2;  // GUID
  string            timestamp                = 3;  // ISO-8601
  optional Pose     pose                     = 4;
  optional Velocity velocity                 = 5;
  TrackPhase        track_phase              = 6;
  repeated string   contributing_information = 7;  // GUIDs
}

A union becomes a oneof

The discriminated unions in the model (for example TargetOrNode) map to a oneof:

message TargetOrNode {
  oneof value {
    Contact   contact   = 1;
    Track     track     = 2;
    Plot      plot      = 3;
    Reference reference = 4;
    Node      node      = 5;
  }
}

Generating and using it

Pull the generated tree for a draft straight from the API and compile it with protoc for your target language:

# Grab the generated .proto tree for the current draft (also includes the
# compiled descriptor.pb and unions.json):
curl -fsSL http://localhost:8817/protobuf/sd3-rc4/bundle.zip -o proto.zip
unzip proto.zip -d proto

protoc --proto_path=proto \
       --python_out=gen \
       --cpp_out=gen \
       $(find proto -name '*.proto')

Or skip code generation entirely and use the codec endpoints — encode a JSON message to binary and decode it back:

# JSON message → protobuf (base64-wrapped, with byte sizes)
curl -fsS -X POST http://localhost:8817/encode \
     -H 'content-type: application/json' \
     -d '{"message": { ... a NODE_STATUS ... }}'

# protobuf (base64) → JSON, re-validated against the schema
curl -fsS -X POST 'http://localhost:8817/decode?message_type=MessageTypeEnum_NODE_STATUS' \
     -H 'content-type: application/json' \
     -d '{"base64": "<payload from /encode>"}'

Because the constraint annotations carried in the IDL (@min, @max, @ext::pattern_*, …) have no native proto3 equivalent, enforce them with a validation layer — the same JSON Schema rules apply to the decoded message (the /decode endpoint does exactly this and returns the validation result). See getting started for validation, and encodings for the JSON and XML projections.

On this page