Skip to content

Latest commit

 

History

History
210 lines (158 loc) · 9.07 KB

README.md

File metadata and controls

210 lines (158 loc) · 9.07 KB

Summary

Definition of the message id and threading decorators.

Motivation

Referring to messages is useful in many interactions. A standard method of adding a message ID promotes good patterns in message families. When multiple messages are coordinated in a message flow, the threading pattern helps avoid having to re-roll the same spec for each message family that needs it.

Tutorial

Message IDs

Message IDs are specified with the @id attribute. The sender of the message is responsible for creating the message ID, and any message can be identified by the combination of the sender and the message ID. Message IDs should be considered to be opaque identifiers by any recipients.

Message ID Requirements

  • Not to exceed 64 characters
  • Sufficiently Unique
  • UUID recommended

Example

{
    "@type": "did:example:12345...;spec/example_family/1.0/example_type",
    "@id": "98fd8d72-80f6-4419-abc2-c65ea39d0f38",
    "example_attribute": "stuff"
}

The following was pulled from this document written by Daniel Hardman and stored in the Sovrin Foundation's protocol repository.

Threaded Messages

Message threading will be implemented as a decorator to messages, for example:

{
    "@type": "did:example:12345...;spec/example_family/1.0/example_type",
    "@id": "98fd8d72-80f6-4419-abc2-c65ea39d0f38",
    "@thread": {
        "thid": "98fd8d72-80f6-4419-abc2-c65ea39d0f38",
        "pthid": "1e513ad4-48c9-444e-9e7e-5b8b45c5e325",
        "seqnum": 2,
        "lrec": 1
    },
    "msg": "this is my message"
}

Thread object

A thread object has the following fields discussed below:

  • thid: The ID of the message that serves as the thread start.
  • pthid: An optional parent thid. Used when branching or nesting a new interaction off of an existing one.
  • seqnum: A message sequence number unique to the thid and sender.
  • lseqnum: A reference to the last message the sender received from the receiver (Missing if it is the first message in an interaction).

Thread ID (thid)

Because multiple interactions can happen simultaneously, it's important to differentiate between them. This is done with a Thread ID or thid.

The Thread ID is the Message ID (@id) of the first message in the thread. The first message may not declare the @thread attribute block, but carries an implicit thid of its own @id.

Sequence Num (seqnum)

Each message in an interaction needs a way to be uniquely identified. This is done with Sequence Num (seqnum). The first message from each party has a seqnum of 0, the second message sent from each party is 1, and so forth. A message is uniquely identified in an interaction by its thid, the sender DID and/or key, and the seqnum. The combination of those three parts would be a way to uniquely identify a message.

Last Received (lrec)

In an interaction, it may be useful to for the recipient of a message to know if their last message was received. A Last Received or lrec value addresses this need, and could be included as a best practice to help detect missing messages.

In the example above, lrec is a single integer value that gives the sequence number of the last message that the sender received before composing their response. This is the most common form of lrec, and would be expected in pairwise interactions. However, n-wise interactions are possible (e.g., in a doctor ~ hospital ~ patient n-wise relationship), and even in pairwise, multiple agents on either side may introduce other actors. This may happen even if an interaction is designed to be 2-party (e.g., an intermediate party emits an error unexpectedly). Thus, lrec supports an extended notation where the value is a struct that operates as a form of vector clock.

In the extended form, lrec is a struct and each key/value pair in the struct is an actor/seqnum, where actor is a DID or a key for an agent:

"lrec": {"did:sov:abcxyz":1, "did:sov:defghi":14}

In the above example, the lrec fragment makes a claim about the last sequence number that was seen by the sender from did:sov:abcxyz and did:sov:defghi. The sender of this fragment is presumably some other DID, implying that 3 parties are participating. If there are more than 3 parties in the interaction, the parties unnamed in lrec have an undefined value for lrec. This is NOT the same as saying that they have made no observable contribution to the thread. To make that claim, use the special lrec value -1, as in:

"lrec": {"did:sov:abcxyz":1, "did:sov:defghi":14, "did:sov:jklmno":-1}

Parties processing lrec should support either the short form or the extended form in all cases; it is always legal to use the extended array form even in a pairwise interaction.

When lrec is omitted, the value of lrec for the thread is undefined.

Example

As an example, Alice is an issuer and she offers a credential to Bob.

  • Alice sends a CRED_OFFER as the start of a new thread, @id=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=0.
  • Bob responds with a CRED_REQUEST, @id=<uuid2>, thid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=0, lrec=0.
  • Alice sends a CRED, @id=<uuid3>, thid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=1, lrec=0.
  • Bob responds with an ACK, @id=<uuid4>, thid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=1, lrec=1.

Nested interactions (Parent Thread ID or pthid)

Sometimes there are interactions that need to occur with the same party, while an existing interaction is in-flight.

When an interaction is nested within another, the initiator of a new interaction can include a Parent Thread ID (pthid). This signals to the other party that this is a thread that is branching off of an existing interaction.

Nested Example

As before, Alice is an issuer and she offers a credential to Bob. This time, she wants a bit more information before she is comfortable providing a credential.

  • Alice sends a CRED_OFFER as the start of a new thread, @id=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=0.
  • Bob responds with a CRED_REQUEST, @id=<uuid2>, thid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=0, lrec=0.
  • Alice sends a PROOF_REQUEST, @id=<uuid3>, pthid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=0.
  • Bob sends a PROOF, @id=<uuid4>, thid=<uuid3>,seqnum=0, lrec=0.
  • Alice sends a CRED, @id=<uuid5>, thid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=1, lrec=0.
  • Bob responds with an ACK, @id=<uuid6>, thid=98fd8d72-80f6-4419-abc2-c65ea39d0f38, seqnum=1, lrec=1.

All of the steps are the same, except the two bolded steps that are part of a nested interaction.

Implicit Threads

Threads reference a Message ID as the origin of the thread. This allows any message to be the start of a thread, even if not originally intended. Any message without an explicit @thread attribute can be considered to have the following @thread attribute implicitly present.

"@thread": {
    "thid": <same as @id of the outer message>,
    "seqnum": 0
}

Implicit Replies

Messages that contain a @thread block with a thid different from the outer message id, but no sequence numbers is considered an implicit reply. Implicit replies have a seqnum of 0 and a lrec of 0. Implicit replies should only be used when a further message thread is not anticipated. When further messages in the thread are expected, a full regular @thread block should be used.

Example Message with am Implicit Reply:

{
    "@id': "<@id of outer message>",
    "@thread": {
    	"thid": "<different than @id of outer message>"
	}
}

Effective Message with defaults in place:

{
    "@id': "<@id of outer message>",
    "@thread": {
    	"thid": "<different than @id of outer message>"
    	"seqnum": 0,
    	"lrec": 0
	}
}

Reference

Drawbacks

Why should we not do this?

Rationale and alternatives

  • Implement threading for each message type that needs it.

Prior art

If you're aware of relevant prior-art, please add it here.

Unresolved questions

  • Using a wrapping method for threading has been discussed but most seemed in favor of the annotated method. Any remaining arguments to be made in favor of the wrapping method?