Copy of Managing Channels
What are pico channels?
A pico receives events and respond to queries on its channels (Event-Query API). A channel on a pico can be viewed as a unique stream of communication with that pico. A pico can have any number of channels. Through the use of policies, type of communication through the channel can be controlled (e.g. only allows events of a certain type/domain to be received on a channel). Pico channels are a type of Event Channels.
A pico channel is represented by an ECI (Event Channel Identifier). It is typically a DID (Decentralized Identifier, e.g. "4DG92vvFFSjxJQiG2sHBgK"). To use a channel, a programmer only needs the ECI itself and the location of the engine the pico is located. Then, following the Sky Event API or Sky Cloud API queries and events can be sent. Mechanisms within KRL have this built in, such as the event library and Wrangler's skyQuery.
Wrangler's Subscriptions use channels to create unique relationships between picos.
A pico's channels as shown in the pico engine UI:
As a quick illustration of the purpose of channels, a pico representing a real-life lamp may wish to make the ability to query whether it is "lit" or "off" to some entities, but reserve the ability to actually change its state to "lit" or "off" to other actors:
For each actor that the lamp pico will give control to, the lamp pico may create a channel that is able to receive "lamp:on" and "lamp:off" events, and then share the created channel with the desired actor. Conversely, for actors that are only allowed to query for the state of the lamp pico, the lamp pico will create channels that only allow an "IsLampOn" query and share that.
Finally, because the lamp pico created a channel for each actor, if any actor misbehaves (e.g. the lamp pico is programmed to stop actors from rapidly flashing the light), the lamp pico can revoke or restrict just that channel, without affecting its communication with any other actor.
Creating a Channel
Channels can be created asynchronously by raising the wrangler:channel_creation_requested event. Wrangler enforces that all channels with the same type must have a unique name. If a type is not supplied to the creation event the channel type will default to "_wrangler". If the client attempts to create a channel with the same name as one that already exists for that type, or no name at all is given, then a system:error event will be raised by Wrangler. On a successful channel creation, a wrangler:channel_created event will be raised by Wrangler.
Creation Example
raise wrangler event "channel_creation_requested" attributes { "name":"test_channel", "type":"testType", "policy_id":"allow-all-policy", "co_id":meta:rid // Added attributes get passed through }
Responding to Creation
rule onNewTestChannel { select when wrangler channel_created where event:attr("co_id") == meta:rid pre { newChannel = event:attr("channel").klog("New channel") channelID = newChannel{"id"} } } /* [KLOG] New channel { "id":"MXvUAUwVzJriPtWCwg9GHG", "pico_id":"cjxuoms8b0022hfbz5yy2bhpz", "name":"test_channel", "type":"testType", "policy_id":"allow-all-policy", "sovrin": { "did":"MXvUAUwVzJriPtWCwg9GHG", "verifyKey":"CC6PWGVLp5Jc7HaKZrsS7fWq5HfhuV2EkewE9ESmAqrK", "encryptionPublicKey":"5LsfhtH42MRmuRWhxQCR7o6gPNM8kqMZo4jeq97BRgoj", "indyPublic":"opEN9Vu6RTd2o4WbxBUM3hHfG1TaeZeZjbzzS1K9rAD" } } */
Deleting a Channel
Not currently implemented as such; but will be
Deleting a channel occurs in much the same way that creating one does. The client raises a wrangler:channel_deletion_requested event with attributes that are either a name and a type, or the ECI of the channel they wish to delete. If invalid attributes are provided a wrangler:channel_deletion_failed event will be raised by Wrangler.
Deletion Example
raise wrangler event "channel_deletion_requested" attributes { "name":"test_channel", "type":"testType", "co_id":meta:rid }
Responding to Deletion
rule onChannelDeleted { select when wrangler channel_deleted where event:attr("co_id") == meta:rid pre { deletedChannel = event:attr("channel").klog("Deleted channel") channelID = deletedChannel{"id"}.klog("Channel ID") } } /* [KLOG] Deleted channel { "id":"MXvUAUwVzJriPtWCwg9GHG", "pico_id":"cjxuoms8b0022hfbz5yy2bhpz", "name":"test_channel", "type":"testType", "policy_id":"allow-all-policy", "sovrin": { "did":"MXvUAUwVzJriPtWCwg9GHG", "verifyKey":"CC6PWGVLp5Jc7HaKZrsS7fWq5HfhuV2EkewE9ESmAqrK", "encryptionPublicKey":"5LsfhtH42MRmuRWhxQCR7o6gPNM8kqMZo4jeq97BRgoj", "indyPublic":"opEN9Vu6RTd2o4WbxBUM3hHfG1TaeZeZjbzzS1K9rAD" } } */
Channel Attributes
A pico channel has certain attributes that each serve a unique purpose in identifying and using that channel. A map containing these attributes is often given to the client by Wrangler, such as when creating and deleting channels.
ID
The channel ID is the DID (Decentralized Identifier) for a channel. It is also known by the initialism "ECI" (Event Channel Identifier). Anyone who knows the ECI and has network access to the engine that the pico with the channel lives on can send events and queries to the channel.
Pico ID
The pico ID is the DID of the pico that the channel exists for. For example, when a query is sent to a pico channel, if the query is allowed by the channel policy, the query will return information as defined by this pico's running KRL code and internal state.
Name
The name of a channel is a way to identify a channel within a particular "type" domain. It allows easier manipulation of the channels that the KRL programmer cares about.
Type
The type of a channel is a way to define a domain for channel names. A natural type for a channel is the meta:rid value, as RIDs are enforced to be unique.
Policy ID
The policy ID of a channel is the engine-unique ID of the "policy" that the channel uses for events and queries. The policy determines what events and queries are allowed on that channel.
Sovrin
The Sovrin information with a channel contains values necessary for creating KRL code for a pico to interact with the Sovrin network.
Policies
Every channel has a policy attached to it. Policies limit which events or queries a channel will accept. They are defined using JSON maps. For events you define which domains and types are allowed and which are denied. Queries use "rid" and "name" instead of "domain" and "type". By default channels have the "allow-all-policy" that accept any events or queries.
For example, a policy that allows all events with domain `foo` and type `bar`. An event `foo/hello` will be rejected. Also all queries will be rejected since the policy didn't define any query rules.
{ "name": "only allow foo/bar events", "event": { "allow": [ // This is a list of rules. // If the event matches one of these rules, the event will be allowed {"domain": "foo", "type": "bar"} ] } }
Now let's try a policy that will allow all events with domain `foo`, but reject `foo/bar`.
{ "name": "allow foo/* but deny foo/bar", "event": { "allow": [ {"domain": "foo"},// omitting `type` means match all {"domain": "aaa", "type": "bbb"}, ], "deny": [ // deny creates a black list of events {"domain": "foo", "type": "bar"} ], } } // foo:foo - allowed // foo:wat - allowed // foo:bar - denied b/c it matches a rule in the `deny` list // zzz:wat - denied b/c domain zzz is not allowed // aaa:bbb - allowed b/c it matched an `allow` rule // aaa:ccc - denied b/c it didn't match an `allow` rule
Now let's allow all events except `system:*` and `danger:nuke`
{ "name": "blacklist system/secret and system/danger", "event": { "allow": [ {}// omitting `domain` and `type` means match all events ], "deny": [ {"domain": "system"}, {"domain": "danger", "type": "nuke"}, ], } } // foo:bar - allowed // hello:system - allowed // system:secret - denied // system:foobar - denied // danger:gun - allowed // danger:nuke - denied
Queries work the same. But use `rid`+`name` instead of `domain`+`type`
The allow all policy looks like this
{ "name": "allow all", "event": { "allow": [{}], }, "query": { "allow": [{}], } }
Creating a Policy
The engine:newPolicy action returns the complete policy that includes it's id
. So the first newPolicy
example (for "only allow foo/bar events") might return
{ "id": "1234", "name": "only allow foo/bar events", "event": { "allow": [ {"domain": "foo", "type: "bar"} ], "deny": [], }, "query": {"allow": [], "deny": []} }
You can then use that returned id to to create a new channel that adopts that policy.
Listing Policies
You can also engine:listPolicies() to see all policies in the engine.
[ {"id": "1234", "name": "policy 1", "event": {"allow": [...]} "query": ...}, {"id": "4321", "name": "policy 2", "event": {"allow": [...]} "query": ...}, {"id": "5555", "name": "policy 3", "event": {"allow": [...]} "query": ...}, ]
Deleting a Policy
To delete a policy use engine:removePolicy(policy_id). It only works if the policy is not in use by any channels.
Taken from the original policies pull request.
Using Channels
List of ways to interact with channels:
- event library
- Allows sending an event to a specific channel on any host, including the local engine.
- If another host is specified will use HTTP.
- HTTP
- Sky Event API
- Sky Cloud API
- skyQuery (which uses the Sky Cloud API)
- The pico engine UI testing tab (which uses Sky Cloud and Sky Event)
- Raising an event in the postlude
- Occurs on the "admin" channel
Channels API
A list of the functions, actions, received events (that Wrangler responds to) and raised events (that Wrangler raises) and in-depth descriptions of when to use them.
API is undergoing development. Incremental changes will be made.
- For all functions, enter the parameters in the order listed.
- Italicized parameters are optional.
- Received Events are events of the "wrangler" domain that Wrangler will respond to
- Raised Events are events that can be selected on within any ruleset in the pico when they occur
- Sent Events are events that the pico will send elsewhere as a result of triggering a Wrangler event.
This is a "one-stop" function for getting desired channel info. Allows grouping of channels by their attributes, or getting specific channels by their ECI/name/etc. List a set of channels depending on what parameters are past. If all parameters are passed as null, it will return all channels for that pico. If the id parameter is passed then the function will always return a single channel based upon the passed value. If the collection parameter is passed, the function will return a map with channels grouped in arrays keyed to the channel attribute passed as the parameter. That is, whatever is passed to collection will group the pico channels according to the value they have as that attribute. If the filtered parameter is passed, the collection parameter must have been passed. The filtered parameter will return an array of channels where the value passed to filtered matches the value of the channel attribute passed to collection. Returns Either A single channel map containing the information for the value passed to id parameter. A map with values that are arrays of channel maps keyed to the channel attribute passed to the collection parameter An array of channel maps whose value for the attribute passed to the collection parameter is the same as the value passed to filtered. NULL if no channels found for any operation. String VALID INPUTS: "id", "pico_id", "name", "type", "policy_id" The channel attribute that you want the value of to be mapped to arrays of channels whose value for that attribute matches A "channel map" looks like this Returns the ECI of the channel whose ECI or name is given to the function. Is identical to only passing one parameter to the channel function. It can be used to check if a channel with that ECI exists. String Returns Either A string containing the ECI of the channel prompted for NULL if no channel is found for the given parameter Returns the name of the channel with the given name or ECI. String Returns Either A string containing the name of the channel prompted for NULL if no channel is found for the given parameter Takes a pico ID, a channel name, a channel type, and optional parameter policy ID to create a new channel for that pico ID with those attributes. The type of new channel This is an arbitrary string typically used to give a domain to channels. The policy ID to apply to the channel Currently defaults to an "allow-all-policy" such that any event can be received on this channel. Returns A map with the following contents id: The new ECI for the created channel Takes a name or ECI of a channel and removes that channel. If a name is given it removes the channel from the pico it is run within. The engine will error if the channel does not exist or you attempt to delete the pico's admin channel. The channel that was removed in a channel map form: Creates a new policy based on a given map argument and returns the created policy. See this pico engine pull for a more in-depth explanation. Example map: The map passed to the action but with an additional "id" key mapped to the policy ID of the newly created policy. Creates a new channel through an event. It first checks that the name passed to it does not exist as a current channel on the pico. If it does not, then it creates the channel. The channel is created with the type and policy given in the event attributes. If no name is passed then an error event is raised. String String Will return empty directives if the name passed already exists as a channel or no name is passed. Corresponding Raised Events: Deletes the channel given as either an ECI or channel name. The engine errors and nothing happens if the channel does not exist. String Will return empty directives if no channel was found to be deleted. Corresponding Raised Events: Raised when a new channel has been created on this pico by using the wrangler:channel_creation_requested event. Channel Map describing the created channel's attributes. A channel map has the structure: An example of a rule selecting on a new channel creation. Raised when a channel has been deleted from this pico using the wrangler:channel_deletion_requested event. Channel Map describing the created channel's attributes. A channel map has the structure: An example of a rule selecting on a channel deletion.Functions
channel
Parameters
Parameter Datatype Description id Can either be the name of a single channel or the ECI of a channel. collection String filtered String The collection parameter must also be passed! This is the channel attribute value that you want to select for response = wrangler:channel(null,null,null); // all channels
response = wrangler:channel("flipper",null,null); // single channel with "flipper" name
response = wrangler:channel(null,"type",null); // collection of channels by type
response = wrangler:channel(null,"type","OAUTH"); // collection of channels with "OAUTH" as type
{
"id": "4PkMF8CYq5dsh7ksn6fDKh",
"pico_id": "cjqr0of8e003p88rq7hric28i",
"name": "admin",
"type": "secret",
"policy_id": "allow-all-policy",
"sovrin": {
"did": "4PkMF8CYq6dsh7ksn6fDKh",
"verifyKey": "2rEaErex4S9CD8y6a1wZuHYxbH5DTpVw7SyBVhCU2gxa",
"encryptionPublicKey": "AmHEeV8dYHZQ69b4wqtdVDUj97KJU4dfuHVoFcZzuvmg"
}
}
alwaysEci
Parameters
Parameter Datatype Description value Can either be the name of a channel or the ECI of a channel. channelECI = wrangler:alwaysEci("wovyn_channel")
nameFromEci
Parameters
Parameter Datatype Description eci Can either be the ECI of a channel or the name of a channel channelName = wrangler:nameFromEci("4PkMF8CYq5dsh7ksn6fDKh")
Actions
createChannel
Parameters
Parameter Datatype Description pico_id String ID of the pico to add the channel to. Will default to the currently running pico. name String The name for the new channel type String policy_id String
pico_id: The pico ID of the pico the channel has been created on
name: The name of the new channel
type: The type of the channel created
policy_id: The policy that describes the channel that has been created//Returns:
{
"id" : <new_eci>,
"pico_id": <pico_id>,
"name": "channel_name",
"type": "channel_type",
"policy_id": "1234"
}
deleteChannel
Parameters
Parameter Datatype Description value String ID of the pico to add the channel to. Will default to the currently running pico. Returns
{
"id": "4PkMF8CYq5dsh7ksn6fDKh",
"pico_id": "cjqr0of8e003p88rq7hric28i",
"name": "admin",
"type": "secret",
"policy_id": "allow-all-policy",
"sovrin": {
"did": "4PkMF8CYq6dsh7ksn6fDKh",
"verifyKey": "2rEaErex4S9CD8y6a1wZuHYxbH5DTpVw7SyBVhCU2gxa",
"encryptionPublicKey": "AmHEeV8dYHZQ69b4wqtdVDUj97KJU4dfuHVoFcZzuvmg"
}
}
newPolicy
Parameters
Parameter Datatype Description policy Map
{
name: "only allow foo/bar events",
event: {
allow: [{domain: "foo", type: "bar"}],
deny: [],
},
query: {allow: [], deny: []}
}Returns
{
id: "1234",
name: "only allow foo/bar events",
event: {
allow: [{domain: "foo", type: "bar"}],
deny: [],
},
query: {allow: [], deny: []}
}
Received Events
channel_creation_requested
wrangler:channel_creation_requested
Attributes
Attribute Datatype Description name The name of the channel you want to create. Must be a unique name from any other channel on the pico. type String The type of channel you want to create. This is an arbitrary string typically used to give a domain to channels. It will default to _wrangler if not given. policy_id The policy ID of the policy you want this channel to follow. Currently defaults to the allow-all-policy. Directives Returned
send_directive("channel_Created", channel);
// Channel is a channel map of the channel with its attributes keyed to their values:
/*
{
"directives": [
{
"options": {
"id": "8nHCdus2sqVmF34SshhtsF",
"pico_id": "cjqszhxzp015fb0rq1istxqhl",
"name": "test",
"type": "test",
"policy_id": "allow-all-policy",
"sovrin": {
"did": "8nHCdus2sqVmF34SshhtsF",
"verifyKey": "5Exq5iXBUQ8XSctTx1156g4kQCSbK62Su2NXHSX8HFkw",
"encryptionPublicKey": "AQwwUCGh9Ke9vKRaGhUj6XkZxfvVS8JdTYyzuaiHiqAF"
}
},
"name": "channel_Created",
"meta": {
"rid": "io.picolabs.wrangler",
"rule_name": "createChannel",
"txn_id": "cjrb9ttiu0023nkrquliin4mj",
"eid": "__testing"
}
}
]
}
*/
channel_deletion_requested
wrangler:channel_deletion_requested
Attributes
Attribute Datatype Description eci The ECI of the channel you want to delete on this pico. name String The name of the channel you want to delete on this pico. Directives Returned
send_directive("channel_deleted", channel);
// Channel is a channel map of the channel with its attributes keyed to their values:
/*
{
"directives": [
{
"options": {
"id": "8nHCdus2sqVmF34SshhtsF",
"pico_id": "cjqszhxzp015fb0rq1istxqhl",
"name": "test",
"type": "test",
"policy_id": "allow-all-policy",
"sovrin": {
"did": "8nHCdus2sqVmF34SshhtsF",
"verifyKey": "5Exq5iXBUQ8XSctTx1156g4kQCSbK62Su2NXHSX8HFkw",
"encryptionPublicKey": "AQwwUCGh9Ke9vKRaGhUj6XkZxfvVS8JdTYyzuaiHiqAF"
}
},
"name": "channel_deleted",
"meta": {
"rid": "io.picolabs.wrangler",
"rule_name": "deleteChannel",
"txn_id": "cjrba64ud0024nkrqm4wp8w4v",
"eid": "__testing"
}
}
]
}
*/
Raised Events
channel_created
wrangler:channel_created
Attributes Added
Attribute Datatype Description channel Map {
"id": "4PkMF8CYq5dsh7ksn6fDKh",
"pico_id": "cjqr0of8e003p88rq7hric28i",
"name": "admin",
"type": "secret",
"policy_id": "allow-all-policy",
"sovrin": {
"did": "4PkMF8CYq6dsh7ksn6fDKh",
"verifyKey": "2rEaErex4S9CD8y6a1wZuHYxbH5DTpVw7SyBVhCU2gxa",
"encryptionPublicKey": "AmHEeV8dYHZQ69b4wqtdVDUj97KJU4dfuHVoFcZzuvmg"
}
}
rule on_new_channel{
select when wrangler channel_created where event:attr("channel"){["type"]} == "test" // If a channel of type test has been created
pre {
}
noop() // Do some operation
fired{
klog("my channel has been created!");
}
}
channel_deleted
wrangler:channel_deleted
Attributes Added
Attribute Datatype Description channel Map {
"id": "4PkMF8CYq5dsh7ksn6fDKh",
"pico_id": "cjqr0of8e003p88rq7hric28i",
"name": "admin",
"type": "secret",
"policy_id": "allow-all-policy",
"sovrin": {
"did": "4PkMF8CYq6dsh7ksn6fDKh",
"verifyKey": "2rEaErex4S9CD8y6a1wZuHYxbH5DTpVw7SyBVhCU2gxa",
"encryptionPublicKey": "AmHEeV8dYHZQ69b4wqtdVDUj97KJU4dfuHVoFcZzuvmg"
}
}
rule on_channel_deletion{
select when wrangler channel_deleted where event:attr("channel"){["type"]} == "test" // If a channel of type test has been deleted
pre {
channelName = event:attr("channel"){["name"]};
}
doCleanup() // Do some operation
fired{
klog("my channel has been deleted!");
ent:channels := ent:channels.delete([channelName]);
}
}
Copyright Picolabs | Licensed under Creative Commons.