Managing Subscriptions

Introduction to Subscriptions

To understand how subscriptions between picos work, it is important to understand what a pico channel is and what channel policies are. Inter-pico Subscriptions use pico channels to implement the subscription protocol itself (which is independent of picos).

Subscriptions allow two picos to communicate with each other, 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 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 known as 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 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. Even then, you should use the family channel to obtain the other pico’s wellKnown channel and then use its ECI to create your subscription.

A wellKnown_Rx can be accessed by querying the wellKnown_Rx() function, shared by the io.picolabs.subscription ruleset. It returns the channel, a Map (with keys: “id”, “tags”, “eventPolicy”, and “queryPolicy”). Of these, you will be interested only in the value of the "id" key, the ECI.

How to obtain a pico's wellKnown channel and its ECI? Once you have a channel, say chan, you get its ECI with chan{"id"}. How you get the channel itself depends on whether you are looking for the well known channel of the same pico, or a different pico.

Another pico

When you are sure that it is a different pico (i.e., not the one running your ruleset), you would use the wrangler:picoQuery() function (documented on the "Accessing a Function Shared by Another Pico" page). For example, code like this will get the ECI of the well known channel of your parent pico:

wellKnown_ECI = wrangler:picoQuery(wrangler:parent_eci(),"io.picolabs.subscription","wellKnown_Rx"){"id"}

Notice that the picoQuery returns the channel as a Map, so you must extract its ECI using the "id" key. Reminder that wrangler:parent_eci() function gives you the family channel ECI for your parent.

And this is how you might get an Array of well known ECIs for all of your child picos:

wrangler:children() // an Array of Maps, each with keys: "eci", "name", and "parent_eci" .map(function(c){ wellKnown_Rx = wrangler:picoQuery(c{"eci"},"io.picolabs.subscription","wellKnown_Rx") // the channel c.put("wellKnown_ECI",wellKnown_Rx{"id"}) // adding a key "wellKnown_ECI" to the map for each child pico }) // an Array of Maps, each with keys: "eci", "name", "parent_eci", and "wellKnown_ECI"

Reminder that the wrangler:children() function gives you the family channel ECI for each of your child picos.

Same pico

Since you can’t use the wrangler:picoQuery() function to call a function in another ruleset of the same pico, you’ll need to use a different technique. The easiest way it to look up the channel by its tags, and then extract the ECI (using the key "id"):

wrangler:channels(["wellKnown_Rx","Tx_Rx"]) // an Array of all your channels with those tags .head() // the first channel with those tags (and usually the only one) {"id"} // this pico's well known ECI for subscription requests

or, without the comments:

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

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.

Requesting to Subscribe

Requesting to Subscribe

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" 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.

Accepting a Request

Reacting to Subscription Creation or a Cancelled Request

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.

Reacting to New Subscription

Advanced Subscription Creation Example

This is an example that is closer to what safe, idiomatic code might look like for creating a relationship between picos using subscriptions. It utilizes different subscription attributes to identify and manipulate the new subscription. Assume the ruleset that these rules are written is installed on both picos.

Advanced Example

Deleting a Subscription

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.

Starting Subscription Cancellation

Reacting to a Subscription Cancellation

Sending Messages Between Subscribers

Manually Sending Messages

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 channel to the pico, or send queries using the wrangler:picoQuery() function (see “Accessing a Function Shared by Another Pico”).

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.

Attempt to Send an Event to Every Subscriber

Send a Query to a Subscriber given Subscription ID

Using an API Call

not 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.

This rule sends an event to a specific subscription ID:

Send an Event to a specific Sub ID

This rule sends an event to all subscriptions that record this pico as a "node" by the Rx_role:

Send an Event to Subs with a specific Rx_role

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 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.


The Tx attribute is the ECI of the other pico's Rx. It is the pico channel that this pico will send events and queries to.


The Rx attribute is the ECI that this pico receives queries and events from the other pico on. It is recorded as the Tx attribute on the other pico. 


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".


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 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

A directional subscription is a subscription that stores roles, providing an easier way to identify relationships (such as master-slave) in a 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.

Cross Engine Subscription

It is easy to imagine a situation where you want to create a subscription between picos hosted on different engines. Cross engine subscriptions allow us to do so by storing the host of the other pico in a 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".

Understanding the Protocol

Subscription Event Flow

Internal API

This section is solely for illustrating how the protocol works under-the-hood. The internal events used are not static and are subject to change, and should not be selected on for long-term code.

Subscription Creation

To establish a basic subscription between pico A and pico B:

1. Pico A receives a wrangler:subscription event which includes a wellKnown_Tx attribute. wellKnown_Tx is Pico B's wellKnown_Rx .  Pico A then creates a channel for the subscription which is stored as Rx, then raises wrangler:subscription_request_needed.

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. name - name used for channel.

e. channel_type - string used for channel type

3. Pico B creates a Rx channel for the subscription which is stored as Rx along with TxTx_verify_keyTx_public_key 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:

a. sends the wrangler:outbound_pending_subscription_approved event to Pico A on Pico B's subscriptions Tx.

b. raises an API event wrangler:subscription_added.

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

If Pico B wants to reject the subscription, it raises the wrangler:inbound_rejection event with an attribute named Rx - which is the Rx of the subscription to reject. Pico B:

a. sends the wrangler:outbound_removal event to Pico A on Picos B Tx ECI, which then raises an API wrangler:outbound_subscription_cancelled event to itself.

b. raises the wrangler:inbound_removal event, which then raises an API wrangler:inbound_subscription_cancelled event.

If Pico A wants to revoke the subscription request, it raises the wrangler:outbound_cancellation event with an attribute named Rx - which is the Rx of the subscription to reject. Pico A:

a. sends the wrangler:inbound_removal event to Pico B on its wellKnown_Rx ECI, which then raises an API wrangler:inbound_subscription_cancelled event to itself.

b. raises the wrangler:outbound_removal event, which then raises an API wrangler:outbound_subscription_cancelled event.

Subscription Deletion

  1. Either pico A or pico B raises a wrangler:subscription_cancellation event. For illustration purposes we will consider it as pico A raising the event, but the process is mirrored either way.

  2. Pico A raises a wrangler:established_removal event within itself and sends another to pico B over the Tx channel.

  3. On reaction to established_removal the channels are deleted and the subscription removed from the list of established subscriptions. wrangler:subscription_removed is then raised with relevant information.

Copyright Picolabs | Licensed under Creative Commons.