Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: update for 1.0.0

ish Table of ContentsmaxLevelish

Table of Contents
maxLevel3

Introduction to Subscriptions

...

Subscriptions allow two picos to communicate with each other on a secure channel, enabling the creation of peer-to-peer structures.  From each pico's perspective there is a receiving channel, Rx, and a transmitting channel, Tx. Each pico receives events and queries from the other pico through the Rx channel and makes queries and sends events to the other pico using the Tx channel. However, it is important to note that there are only two pico channels, just a different perspective. For example, Pico A records a Tx channel and an Rx channel, but the Tx channel that pico A has recorded is actually Pico B's Rx channel and vice versa.

...

Subscriptions can occur between any two picos (and even one pico with itself) as long as the picos have a way to communicate (over HTTP or on the same engine). For example, in the Forever application, the picos belonged to different people and represented their personal contact information. The io.picolabs.subscription ruleset is what implements subscriptions, so both picos need to have it installed.

Subscriptions are established by pico A sending a request to establish a subscription to pico B, and pico B then accepting that request.

Well Known Rx

When the io.picolabs.subscription ruleset is installed on a pico (it is preinstalled on every pico created by Wrangler), it creates a pico channel named the "wellKnown_Rx" channel. This channel is what receives requests to create  subscriptions. The channel has a policy that only allows events related to accepting a subscription. This allows the DID ( ECI ) of this "wellKnown" channel to be shared publicly with anyone without fear that someone will send events that mess with the pico or send queries for information they are not allowed to have. Although any channel which does not block the subscription events will work when creating subscriptions, it is considered good practice to use the wellKnown_Rx channel unless the subscription is between a parent and child pico.

As a reminder, a channel contains three pieces of information: the Event Channel Identifier (ECI), a nametags, and a descriptive typepolicies. The well known channel name is tags are wellKnown_Rx with Tx_Rx as its channel typeThis channel is used to establish new subscriptions between picos, and is meant to be publicly shared with potential subscribers.

A wellKnown_Rx can be accessed by querying the subscriptions wellKnown_Rx() function.

Creating a Subscription

...

A wrangler:subscription event is what initiates a subscription request. You raise it within the pico that is going to send the request to the other pico. Inside of the event you give attributes that describe the type of subscription you want to create. Any attributes added to this event will be seen by the pico receiving the request, which allow picos that receive requests to receive meta information about the requesting pico.

...

As soon as the above event is raised, Wrangler will initiate the process. If all goes well, the receiving pico will have a wrangler:inbound_pending_subscription_added event raised within it.


Accepting a Subscribe Request

To accept (or decline) a subscription request, the receiving pico should listen for the wrangler:inbound_pending_subscription_added event. This event will provide meta information about the type of subscription the other pico wants to form, as well as an attributes that were passed to the wrangler:subscription event that initiated the subscription. 

To accept the subscription, the client only need raise the wrangler:pending_subscription_approval event in response to the wrangler:inbound_pending_subscription_added event, with either the ID "Id" of the soon-to-be subscription or the ECI of the Rx that this pico will receive subscription messages on. Both of these things are already attributes from the wrangler:pending_subscription_approval event, so a simple raise wrangler event "pending_subscription_approval" attributes event:attrs; works just fine.

...

As soon as a subscription is created, a KRL developer may want to save that subscriptions info so that they can start utilizing the subscription for communication, or immediately send a message on the subscription. The developer may react to the wrangler:subscription_added event for this purpose. The event contains attributes that provide the new subscription's attributes and any attributes that were passed through the subscription creation event chain.

Code Block
languagejs
themeConfluence
titleReacting to New Subscription
/* Our rule that reacts to a new subscription being added and records it in an entity variable */
rule newSubAdded {
  select when wrangler subscription_added
  pre {
    subID = event:attr("Id")                // The ID of the subscription is given as an attribute
    subInfo = event:attr("bus")             // The relevant subscription info is given in the "bus" attribute
  }
  always {
    ent:subs{subID} := subInfo              // Record the sub info in this ruleset so we can use it
  }
}

/* This rule sends an event to each subscription we know about when a new subscription is added */
rule sendMsgToSubs {
  select when wrangler subscription_added
  foreach ent:subs.values() setting (sub)
  event:send({
    "eci":sub{"Tx"},                        // The "Tx" in the subscription info is the other pico's receiving channel (Rx), which takes our events and queries!
    "eid":"sending_info",                   
    "domain":"example",                     // The domain of our event (needs to be allowed by subscription policy, which by default is allow-all)
    "type":"example_message",               // The event type of our event
    "attrs":{                               // The attrs of our event
      "message":"Hello subscribers!"
    }
  })
}

...

To delete or cancel a subscription the KRL developer raises a wrangler:subscription_cancellation event with attributes that describe the target subscription to cancel. As a result, a wrangler:subscription_removed event will be raised that the developer can react to.

...

Code Block
languagejs
themeConfluence
titleReacting to a Subscription Cancellation
/*Turns a pico white in the UI when a subscription has been removed*/
rule whenSubCancelled {
  select when wrangler subscription_removed
  always {
    raise visualengine_ui event "updatebox" attributes {
    "colorbackgroundColor":"#FFFFFF",
	"dname":wrangler:myself(){"name"}
    }
  }
}

Sending Messages Between Subscribers

...

At any time the KRL developer can get an array of all established subscriptions by querying the established() function in the io.picolabs.subscription ruleset. Each member of the array will have a map giving all needed information about the subscription (descriptions of these attributes are given in the Subscription Attributes section). The "Tx" attribute in this map is the ECI of the other pico's channel for receiving events and queries. The developer can use this value with event:send() to send events valid for the subscription channel to the pico, or send queries using skyQuery/HTTP.

However, using the established() function is not necessary. A developer could manage subscriptions that only they care about by saving the subscription information when a new subscription is created by reacting to the wrangler:subscription_added event.

Code Block
languagejs
themeConfluence
titleAttempt to Send an Event to Every Subscriber
/* In the meta block: "use module io.picolabs.subscription alias subscription" */
/* This rule attempts to send an event to each subscription this pico has */
rule sendMsgToSubs {
  select when example send_msg
  foreach subscription:established() setting (sub)
  event:send({
    "eci":sub{"Tx"},                        // The "Tx" in the subscription info is the other pico's receiving channel (Rx), which takes our events and queries!
    "eid":"sending_info",                   
    "domain":"example",                     // The domain of our event (needs to be allowed by subscription policy, which by default is allow-all)
    "type":"example_message",               // The event type of our event
    "attrs":{                               // The attrs of our event
      "message":"Hello subscribers!"
    }
  })
}

...

Code Block
languagejs
themeConfluence
titleSend a Query to a Subscriber given Subscription ID
/* In the meta block: "use module io.picolabs.subscription alias subscription" */
/* Given a valid Sub ID get the name of the other pico */
rule queryPeer {
  select when update get_peer_info
  pre {
    subID = "cjy4yp73q00940sbz9ocy7h5c"                                                           // Given a valid Sub ID
    peerSubs = subscription:established("Id", subID)                                              // Get established subscriptions that match that ID
    sub = peerSubs.head()                                                                         // Returned an array so we get the first sub
    peerChannel = sub{"Tx"}                                                                       // Get the "Tx" channel to send our query to
    peerHost = (sub{"Tx_host"} || meta:host)                                                      // See if the pico is on a different host. If it isn't use this engine's host
    peersInfo = wrangler:skyQuery(peerChannel, "io.picolabs.wrangler", "myself", null, peerHost)  // Actually query the pico with the information we've found
    peersPicoName = peersInfo{"name"}.klog("pico name is")                                        // Use the returned query info to get the info we want
  }
}

Using an API Call

Warning
titlenot yet tested

The rule that reacts to a wrangler:send_event_on_subs event has not yet been tested in pico-engine version 1.0.0


Subscriptions also provides an API event that the KRL developer can raise to send an event on a subscription. The developer can choose to either send an event to a specific subscription ID, to all subscriptions with a certain Tx_role, or all subscriptions with a certain Rx_role, or a mixture of the three.

...

Code Block
languagejs
themeConfluence
titleSend an Event to Subs with a specific Rx_role
/* Send a wrangler:child_creation event to all subscriptions where this pico is a node */
rule send_to_all_nodes {
  select when nodes send_event_to_nodes
  always {
    raise wrangler event "send_event_on_subs" attributes {
      "domain":"wrangler",
      "type":"new_child_creationrequest",
      "Rx_role":"node",
      "attrs":{
        "name":"CreatedBySubscription"
    }
  }
}

Sending Signed and Encrypted Messages

Channels have built in private/public key pairs and an established subscription has a shared secret to support symmetric encryption of events. For a more in-depth article: Sending Secure Messages. For documentation on the functions themselves see: engine.

Manually Signing/Verifying

Signing and Verification is done using two functions: engine:signChannelMessage(Rx, message) and engine:verifySignedMessage(Tx_verify_key, signed_message). They will return false on failure, and the signed/verified message on success. The "Rx" in each function's parameters is the Rx for the relevant subscription on the pico. The Tx_verify_key parameter is a subscription attribute that can be found when you query for info on a subscription.

Manually Encrypting/Decrypting

...

      "name":"CreatedBySubscription"
    }
  }
}

Subscription Attributes

...



Subscriptions come with a few related attributes that allow for different ways of using subscriptions. Italicized attributes may be NULL.

...

The ID of a subscription identifies a subscription relationship. By default it is a DID (decentralized identifier) globally unique identifier assigned by wrangler, but it can be set by the developer in the initial wrangler:subscription event. Both picos record the same ID for the subscription, as the ID represents the relationship itself.

...

When creating a subscription you can store "roles" for each pico. This makes it easy to identify subscriptions with special types of relationships, such as a master-slave relationship between two picos. The Tx_role attribute is the role that the other pico has in the subscription relationship. Hence "Transmitting channel role".

...

When creating a subscription you can store "roles" for each pico. This makes it easy to identify subscriptions with special types of relationships, such as a master-slave relationship between two picos. The Rx_role attribute is the role that this pico has in the subscription relationship. Hence "Receiving channel role".

Tx_verify_key

The Tx_verify_key is the verification public key of the other pico. It can be used to verify messages signed by the other pico.

Tx_public_key

The Tx_public_key is the encryption public key of the other pico. It can be used to encrypt and decrypt messages to be sent between the two picospico has in the subscription relationship. Hence "Receiving channel role".

Tx_host

The Tx_host attribute is the network location of the engine the other pico is running on. This allows cross-engine subscriptions. If this attribute is null then the other pico is on the same engine. When manually sending messages the developer should check if this is null and use it's its value to send the message if it is not null. The event:send action does this check for you.

Types of Subscriptions

...

Directional Subscription

...

To create a directional subscription request from pico A to pico B. Raise a wrangler:subscription event to pico A with additional attributes, Rx_role, Tx_role.  Rx_role is pico A's role in the subscription, leaving Tx_role to be pico B's role in the subscription relationship.

...

To create a cross engine subscription between pico A and pico B, raise a wrangler:subscription event to pico A with event attribute Tx_host.  Tx_host is pico B's engine host url including protocol, for example, "http://localhost:8282".

...

2. Pico A sends pico B wrangler:new_subscription_requestPico A sends all attributes it received in wrangler:subscription with the following key attributes updated:

a. Tx - Pico A Rx.

b. Tx_verify_key - Pico A's Rx channel verifyKey.

c. Tx_public_key - Pico A's Rx channel encryptionPublicKey.

d. name - name used for channel.

...

3. Pico B creates a Rx channel for the subscription which is stored as Rx along with TxTx_verify_keyTx_public_key in inbound in the ent:inbound subscription entity variable.

...

4. After storing pending subscriptions, Pico B and Pico A raises corresponding wrangler:inbound_pending_subscription_added event and wrangler:outbound_pending_subscription_added event.
The state of each Picos subscription is static at this point. Pico A has an outgoing pending subscription, with relevant info stored in the ent:outbound subscription entity variable, and Pico B has an incoming pending subscription with relevant info stored in the ent:inbound subscription entity variable.

...

5. If Pico B wants to approve the subscription request, it raises the wrangler:pending_subscription_approval event with an attribute Rx containing the Rx channel identifier of the subscription to approve.  Upon successfully storing the subscription in ent:established entity variable, pico B:

...

6. Upon receiving the wrangler:outbound_pending_subscription_approved event, Pico A adds an Tx eci to the subscription and stores the subscription in established  ent:established entity variable.  And raises the wrangler:subscription_added event.

...