I'm excited to announce a new, stable, production-ready pico engine. The latest release of the Pico Engine (1.X) provides a more modular design that better supports future enhancements and allows picos to be less dependent on a specific engine for operation.
The pico engine creates and manages picos.1 Picos (persistent compute objects) are internet-first, persistent, actors that are a good choice for building reactive systems—especially in the Internet of Things.
The 2017 rewrite (Pico Engine 0.X) was a great success. When we started that project, I listed speed, internet-first, small deployment, and attribute-based event authorization as the goals. The 0.X rewrite achieved all of these. The new engine was small enough to be able to be deployed on Raspberry Pi's and other small computers and yet was significantly faster. One test we did on a 2015 13" Macbook Pro handled 44,504 events in over 8000 separate picos in 35 minutes and 19 seconds. The throughput was 21 events per second or 47.6 milliseconds per request.
This past year Matthew and Bruce reimplemented the pico engine with some significant improvements and architectural changes. We've released that as Pico Engine 1.X. This blog post discusses the improvements in Pico Engine 1.X, after a brief introduction of picos so you'll know why you should care.
Picos support an actor model of distributed computation. Picos have the following three properties. In response to a received message,
- picos send messages to other picos—Picos respond to events and queries by running rules. Depending on the rules installed, a pico may raise events for itself or other picos.
- picos create other picos—Picos can create and delete other picos, resulting in a parent-child hierarchy of picos.
- picos change their internal state (which can affect their behavior when the next message received)—Each pico has a set of persistent variables that can only be affected by rules that run in response to events.
Despite the parent-child hierarchy, picos can be arranged in a heterachical network for peer-to-peer communication and computation. As mentioned, picos support direct asynchronous messaging by sending events to other picos. Picos have an internal event bus for distributing those messages to rules installed in the pico. Rules in the pico are selected to run based on declarative event expressions. The pico matches events on the bus with event scenarios declared in the event expressions. Event expressions can specify simple single event matches, or complicated event relationships with temporal ordering. Rules whose event expressions match are scheduled for execution. Executing rules may raise additional events. More detail about the event loop and pico execution model are available elsewhere.
Each pico presents a unique Event-Query API that is dependent on the specific rulesets installed in the pico. Picos share nothing with other picos except through messages exchanged between them. Picos don't know and can't directly access or affect the internal state of another pico.
As a result of their design, picos exhibit the following important properties:
- Lock-free concurrency—picos respond to messages without locks.
- Isolation—state changes in one pico cannot affect the state in other picos.
- Location transparency—picos can live on multiple hosts and so computation can be scaled easily and across network boundaries.
- Loose coupling—picos are only dependent on one another to the extent of their design.
Pico Engine 1.0
Version 1.0 is a rewrite of pico-engine that introduces major improvements:
- A more pico-centric architecture that makes picos less dependent on a particular engine.
- A more module design that supports future improvements and makes the engine code easier to maintain and understand.
- Ruleset versioning and sharing to facilitate decentralized code sharing.
- Better, attribute-based channel policies for more secure system architecture.
- A new UI written in React that uses the event-query APIs of the picos themselves to render.
One of our goals for future pico ecosystems is build not just distributed, but decentralized peer-to-peer systems. One of the features we'd very much like picos to have is the ability to move between engines seamlessly and with little friction. Pico engine 1.X better supports this roadmap.
pico-framework handles the building blocks of a Pico based system:
- Pico lifecycle—picos exist from the time they're created until they're deleted.
- Pico parent/child relationships—Every pico, except for the root pico, has a parent. All picos may have children.
- Events—picos respond to events based on the rules that are installed in the pico. The
pico-frameworkmakes use of the
select_whenlibrary to create rules that pattern match on event streams.
- Queries—picos can also respond to queries based on the rulesets that are installed in the pico.
- Channels—Events and queries arrive on channels that are created and deleted. Access control policies for events and queries on a particular channel are also managed by the
- Rulesets—the framework manages installing, caching, flushing, and sandboxing rulesets.
- Persistence—all picos have persistence and can manage persistent data. The
pico-frameworkuses Levelup to define an interface for a LevelDB compatible data store and uses it to handle persistence of picos.
pico-framework is language agnostic.
Pico-engine-core contains a registry (transparent to the user) that caches compiled rulesets that have been installed in picos. In addition,
pico engine combines the
pico-engine-core with a LevelDB-compliant persistent store, an HTTP server, a log writer, and a ruleset loader for full functionality.
Wrangler is the pico operating system. Wrangler presents an event-query API for picos that supports programatically managing the pico lifecycle, channels and policies, and rulesets. Every pico created by the pico engine has Wrangler installed automatically to aid in programatically interacting with picos.
One of the goals of the new pico engine was to support picos moving between engines. Picos relied too heavily on direct interaction with the engine APIs in 0.X and thus were more tightly coupled to the engine than is necessary. The 1.0 engine minimizes the coupling to the largest extent possible. Wrangler, written in KRL, builds upon the core functionality provided by the engine to provide developers with an API for building pico systems programmatically. A great example of that is the Pico Engine Developer UI, discussed next.
Pico Engine Developer UI
Another significant change to the pico engine with the 1.0 release was a rewritten Developer UI. In 0.X, the UI was hard coded into the engine. The 1.X UI is a single page web application (SPA) written in React. The SPA uses an API that the engine provides to get the channel identifier (ECI) for the root pico in the engine. The UI SPA uses that ECI to connect to the API implemented by the
io.picolabs.pico-engine-ui.krl ruleset (which is installed automatically in every pico).
Figure 2 shows the initial Developer UI screen. The display is the network of picos in the engine. Black lines represent parent-child relationships and form a tree with the root pico at the root. The pink lines are subscriptions between picos—two-way channels formed by exchanging ECIs. Subscriptions are used to form peer-to-peer (heterachical) relationships between picos and do no necessarily have to be on the same engine.
When a box representing a pico in the Developer UI is clicked, the display shows an interface for performing actions on the pico as shown in Figure 3. The interface shows a number of tabs.
- The About tab shows information about the pico, including its parent and children. The interface allows information about the pico to be changed and new children to be created.
- The Rulesets tab shows any rulesets installed in the pico, allows them to be flushed from the ruleset cache, and for new rulesets to be installed.
- The Channels tab is used to manage channels and channel policies.
- The Logging tab shows execution logs for the pico.
- The Testing tab provides an interface for exercising the event-query APIs that the rulesets installed in the pico provide.
- The Subscriptions tab provides an interface for managing the pico's subscriptions and creating new ones.
Because the Developer UI is just using the APIs provided by the pico, everything it does (and more) can be done programatically by code running in the picos themselves. Most useful pico systems will be created and managed programmatically using Wrangler. The Developer UI provides a convenient console for exploring and testing during development. The
io.picolabs.pico-engine-ui.krl ruleset can be replaced or augmented by another ruleset the developer installs on the pico to provide a different interface to the pico. Interesting pico-based system will have applications that interact with their APIs to present the user interface. For example, Manifold is a SPA written in React that creates a system of picos for use in IoT applications.
In addition to the work on the engine itself, one of the primary workstreams at present is to complete Bruce Conrad's excellent work to use DIDs and DIDComm as the basis for inter-pico communication, called ACA-Pico (Aries Cloud Agent - Pico). We're holding monthly meetings and there's a repository of current work complete with issues. This work is important because it will replace the current subscriptions method of connecting heterarchies of picos with DIDComm. This has the obvious advantages of being more secure and aligned with an important emerging standard. More importantly, because DIDComm is protocological, this will support protocol-based interactions between picos, including credential exchange.
- The pico engine is to picos as the docker engine is to docker containers.
Photo Credit: Flowers Generative Art from dp792 (Pixabay)