Distributed Publish & Subscribe for IoT
Message Types and Flow


DPS has four message types: subscriptions, publications, acknowledgments, and subscription acknowledgements.

Subscriptions and publications are both inherently point-to-multipoint. An explicit assumption is that in IoT use cases there are many more publishers than subscribers. Publications are sent fairly frequently but subscriptions are relatively stable; subscriptions do not change frequently. To a large extent DPS has been designed around these assumptions. DPS would be good for implementing an IoT network with a large number of sensors but not ideal for implementing a highly scalable peer-to-peer chat service.

When a subscription does change only deltas for the subscription propagate through the network, this is typically less than a 100 bytes. Subscription acknowledgement messages are used to confirm that subscriptions are received at the next hop.

Publications are routed to all subscribers that have subscription topics that match the publication topics as described above.

Publications and acknowledgments can be accompanied by a payload, subscriptions do not carry a payload. Acknowledgements are optional and must be explicitly requested by the publisher when sending a publication. A subscriber can send an acknowledgement to the publisher along with an optional payload, the acknowledgement reaches the publisher by hop-by-hop forwarding in the reverse path of the publication. The reverse path ages out fairly quickly so acknowledgements should be sent as soon as possible after receipt of a publication. A publisher may receive multiple acknowledgments if there are multiple subscribers.

Encoding

DPS messages are encoded in CBOR, described below in CDDL.

Each message has the same form.

message = [
  version: 1,
  type: pub / sub / ack / sak,
  unprotected: { * field },
  protected: { * field },
  encrypted: { * field }
]

Field member keys

For compactness the member keys are encoded as integers as listed below.

field = (
  ? 1 => uint,               ; # port number sender is listening on
  ? 2 => int,                ; # ttl - time to live in seconds
  ? 3 => uuid,               ; # pub-id - unique identifier for a publication
  ? 4 => uint,               ; # seq-num - sequence number for a publication
  ? 5 => bool,               ; # ack-req - indicates if an publisher is requesting an acknowledgement
  ? 6 => bit-vector,         ; # bloom-filter -the bloom filter for a publication
  ? 7 => sub-flags,          ; # sub-flags - indicates delta or mute
  ? 8 => uuid,               ; # mesh-id - the mesh ID
  ? 9 => bstr,               ; # needs - the needs bit vector fuzzy hash
  ? 10 => bit-vector,        ; # interests - the interests bit vector
  ? 11 => [ + topic: tstr ], ; # topics - the topic strings
  ? 12 => bstr,              ; # data - payload data
  ? 13 => uint,              ; # ack-seq-num - sequence number for an acknowledgement
  ? 14 => tstr,              ; # path - path sender is listening on
  ? 15 => uint               ; # hop-count - number of hops this message has traveled
)

The description of each message type includes what fields are mandatory or optional for each section.

UUID

UUIDs identify publications and are also used as key identifiers for encrypted messages.

uuid = bstr .size 16

Bit vector encoding

The bit vector encoding includes control flags, the bit vector length expressed in bits and the raw or run-length encoded bit vector data.

bit-vector = [
  flags: uint .bits bit-vector-flags, ; # bit vector control flags
  len: uint,                          ; # bit vector length in bits
  bits: bstr                          ; # raw or rle-encoded bit vector
]

Bit vector control flags

Bits vectors are usually run-length encoded unless the raw unencoded bit vector is more compact than the rle-encoded representation. The rle-encoded flag indicates if the bit vector is encoded or raw.

The rle-complement flags indicates if the complement of the bit vector was was encoded. The bit vector complement is encoded if this results in a more compact encoding. This flag is only useful with run-length encoding.

bit-vector-flags = &(
  rle-encoded: 1,
  rle-complement: 2
)

Subscription flags

sub-flags = &(
  delta: 1,        ; # indicate interests is a delta
  mute: 2,         ; # mute has been indicated
  sak-req: 4       ; # an acknowledgement is requested
)

Publication message

pub = 1

ttl, hop-count and one of port or path are mandatory in the unprotected section.

ttl, pub-id, seq-num, ack-req and bloom-filter are mandatory in the protected section.

topics and data are mandatory in the encrypted section.

Subscription message

sub = 2

seq-num and one of port or path are mandatory in the unprotected section.

Additionally, in a regular subscription message, sub-flags, mesh-id, needs and interests are mandatory in the unprotected section. In an unlink subscription message those fields shall be absent.

Acknowledgement message

ack = 3

pub-id and ack-seq-num are mandatory in the protected section.

data is optional in the encrypted section.

Subscription acknowledgement message

sak = 4

ack-seq-num and one of port or path are mandatory in the unprotected section.