Created: August 9, 2022
Status: Draft
Across a wide array of decentralized app, service, and protocol scenarios there exists a need for entities to expose various interactions to the public and shape interactions that adhere to expected schemas, data relationships, and protocol flows (e.g. tbDex). The following proposal describes a mechanism for defining protocols and associated logic DWeb Nodes use to address these needs in an extensible way that covers the vast majority of app and service use cases.
When thinking about apps and services as protocols, most apps and services have some aspects of interactions between participants that is open to the public and must follow certain business rules for how data is related and handled. Being able to expose open initiation of app or service flows is something you see with Twitter (anyone can reply to your tweets), credential issuance (anyone can apply for a credential), and tbDEX (anyone can submit an Ask), to name a few. Additionally, in virtually all apps and services there exist relationships between data/objects that define what objects are attached to others, and who can perform certain actions within a flow. Due to the fact both credential issuance and tbDEX require both of the technical capabilities outlined above, we need to put such a mechanism in place to enable them.
The core of this proposal is to create a new interface that enables the static declaration of a protocol’s structure, the allowed interactions, and enforced constraints. This would manifest as the following set of interface methods:
ProtocolsConfigure
The ProtocolsConfigure
interface method installs a configuration of a protocol definition in a DWeb Node. The definition installed is used to gate ingest of different records, sets forth the relationships between said records, and determines what actions an outside entity can take in relation to the records and data model it defines.
{
method: "ProtocolsConfigure", // required
protocol: "identity.foundation/protocols/credential-issuance", // required
version: "1.0.0", // required
definition: { PROTOCOL_DEFINITION_OBJ }, // optional
lastConfiguration: CID_OF_PREVIOUS_CONFIG, // required if previous exists
retainedRecords: CHAMP_OF_INCLUDED_ITEMS // optional
}
The evaluation logic for configurations of protocols must be constructed such that conflicting versions of the same protocol cannot be active at the same time. To do this configurations of the same version must form a linked list from one configuration to another. Any root or branch-level conflicts between configuration entries will be resolved via the simple lexicographic comparison of their CIDs, proceeding with the greatest value and discarding others.
Here is the lifecycle of a protocol envisioned from initial configuration addition to closure of a version:
lastConfiguration
value is present).ProtocolConfigure
message that sets the same tuple, includes the new definition, references the last configuration being superseded, and includes any record references to be retained in relation to that configuration. (consider using a CHAMP for the retainedRecords
value)retainedRecords
value in the new configuration entry.ProtocolsQuery
The ProtocolsQuery
interface method allows an outside entity to query for any active protocols the owner has an active configuration for.
{
method: "ProtocolsQuery",
protocol: "identity.foundation/protocols/credential-issuance" ???
versions: [???], // optional, absence = return all active versions
active: true / false
}
Protocol definitions are objects that set forth the rules for handling of records that invoke a given protocol version. They determine acceptance of records by external entities, how records must relate to one another, and what external entities can do in relation to those records.
Example of a credential issuance flow modeled as a protocol definition:
{
method: "ProtocolsConfigure",
protocolDefinition: {
"objects": {
"credentialApplication": {
"schema": "https://identity.foundation/schemas/credential-application"
},
"credentialResponse": {
"schema": "https://identity.foundation/schemas/credential-response"
}
},
"structure": {
"credentialApplication": {
"encryptionRequired": true,
"allow": { // Issuers would have this allow present
"anyone": {
"to": [
{
"write": {
"published": true
}
}
]
}
},
"records": {
"credentialResponse": {
"allow": {
"recipient": {
"of": "credentialApplication",
"to": [
{ action: "create", }
]
}
}
}
}
}
}
}
}
Recursive example:
{
"protocol": "https://protocols.org/dwitter/v1.1.0",
"objects": {
"ask": {
"schema": "https://tbdex.orb/protocol/ask"
},
"conditionalOffer": {
"schema": "https://tbdex.orb/protocol/conditionalOffer"
},
"close": {
"schema": "https://tbdex.orb/protocol/close"
},
},
"structure": {
"ask": {
"allow": {
"anyone": {
"to": [
{ action: "create" }
]
},
"records": {
"conditionalOffer": {
"allow": {
"recipient": {
"of": "ask",
"to": [
{ action: "create", penalty: "block" }
]
}
}
},
"close": {
"allow": {
"participants": {
"to": [
{ action: "create" }
]
}
}
}
}
}
}
}
}
Graph representations of contextual protocol invocations:
{
"credentialApplication": {
"__value__": ASK_DATA_OR_REF,
"conditionalResponse": {
"__value__": OFFER_DATA_OR_REF
}
}
}
The process of evaluation is envisioned as follows:
authorization
invocation of the protocol/version tuple.allow
directives that pertain the record.{
descriptor: {
method: "CollectionsWrite",
protocol: "identity.foundation/protocols/credential-issuance",
protocolVersion: "1.0.0",
contextId: ???,
schema: "https://identity.foundation/schemas/credential-application",
published: true, // must be set if anyone: { to: ['read'] }
},
authorization: { INVOKE_CID_OF_CONFIGURATION }
}