TL; DR
The Perspectives Distributed Runtime (PDR) is an Electron Application. It is written in the functional language Purescript (a strict interpretation of Haskell that is compiled to Javascript) and has an HTML interface written in React. It stores data in a local installation of Couchb. The runtime loads and unloads data dynamically on demand. It also parses three languages, translating them into an internal representation. It serialises this internal representation as JSON. Logically, it receives streams of mutations to contexts and roles and maintains its own copy of that data (its Bubble). We discuss the Perspectives Exchange Protocol and the Confidentiality, Integrity and Availability of data.
The main functions of the core
‘The Core’ is not exactly the same as the PDR, but it is close. The PDR is the entire Elektron application plus the Couchdb database; the core is the Elektron client process.
Marshall Contexts in and out of memory
The user’s Bubble is the collection of contexts he plays a user-role in and all roles in those contexts that he has a perspective on (that is, his own role in such a context has actions that enable him to ‘see’ those other roles). Now, over time, this collection can grow quite large. Much larger than is reasonable to expect can be loaded into working memory altogether. Luckily, there is no use case for such a scenario. The common case is that the user moves from context to context, with a surrounding field of other contexts. So an important function of the Core is to keep track of those ‘active’ contexts. It loads the JSON representation of such contexts (and roles) into memory, parsing them and representing them internally. It also unloads contexts and roles when it guesses they are no longer needed. If it guessed wrong, it will reload them. This process of loading and reloading is quite autonomous. It is also completely asynchronous with respect to all other functions. This means, of course, that all other processes must be interruptible. This was one of the reasons for choosing Purescript to implement the Core.
Keep queries up to date
Elsewhere we have described how the user interface of the PDR rests on a collection of queries (exactly what a query is, is explained here. These queries can be understood as database views: it is important they be kept up to date as the underlying contexts, roles and properties change. We achieve good performance by employing dependency tracking. This is a form of dependency administration. If a query result depends on a particular property, for example, we register that property as a support for the query result. Then, as the property changes, we propagate the change through the dependency network and thereby update the query result.
Handle streams of updates
Logically, the Core is a module that accepts two streams of updates. An update describes an elementary change to a property, or the creation or deletion of a role or a context, or the filling or emptying of a role by another role (see the Perspectives Exchange protocol below). The primary stream comes from the user (see the section on ‘Interfaces’ for details on this stream), as he interacts with his interface (a graphical screen in case of the PDR).
The secondary stream is fed by all other users. In the end, all data comes from users! However, updates from other users are checked with the governing model. Is the other user in a role that has an action to perform that update? If not, the update is rejected.
Again, these two streams operate asynchronously and updates are handled asynchronous.
Persist updates
An update is first translated into an in-memory representation. Then – again asynchronously – this update leads to a change of the persistent JSON data in couchdb. The internal representations of contexts and roles will be serialised as JSON. This is the same for updates coming from the ‘own’ user, and other users. This is a straightforward function that uses the HTTP interface of Couchdb. Again, this is an asynchronous process. Changes may be bundled for performance reasons.
Parse CRL, PRL and PDL
The core has parsers for three languages. The most basic language is the Context Role Language. This language is close to the internal representation. Even so, strings representing expressions in this language must be parsed and then represented internally. The Perspectives Representation Language (PRL) is much nearer the modellers conception of contexts, roles, properties, views, actions and what have you. Again, strings representing expressions in this language are compiled. This parser works incremental, so it efficiently translates small updates to a text to changes in the internal representation. It was created specifically to complement a text editor used to enter a model. It is an important part of the support we give to modellers in the IDE Atom (and VsCode).
The third parser accepts modifications to diagrams representing a Perspectives Model (PDL).
Again, all changes to the internal representation that arise in response to parsing expressions in these languages, are persisted into Couchdb.
Cores and Couchdb
A user may simultaneously use several devices (e.g. a laptop and a smartphone). Each device will have its own local couchdb. Each device will handle updates by itself. So there is no sycnhronisation of a user’s couchdb copies through couchdb’s own update protocol.
It is possible to run multiple clients on a core, but we do not support multiple cores connecting to a single couchdb.
Interfaces
The Core connects to the outside world on three interfaces. First we have the Couchdb HTTP interface. The core listens for updates on a ‘post’ database. This is the database that other PDR’s push update messages into.
Then there is an in-process channel that consists of foreign function interface (FFI) calls. This is used in the PDR to connect to the user interface. Remember that the Core is a Purescript program compiled into Javascript. This Javascript program runs on the page that is rendered by the Elektron program. The user interface runs on that page, too. It has its own Javascript program and the two connect through a Javascript library provided by Perspect IT. This library provides the programmer with components to navigate through the modelled structure of contexts and roles. It also interfaces to the core and is alerted when query results change. These changes then propagate in the familiar React-fashion to the display on screen. Inversely, when the user enters information on screen, the library transports this in the form of an update to the core that handles it in the way described above.
There is also an out-of-process channel that uses TCP sockets. The language server – running in a process of its own – that provides the modeller with feedback while he types, uses this interface.
In the core, the differences between these two interfaces are abstracted away behind notions of a coroutine library. Both interfaces are ‘producers’ to this library. The same holds for the Couchdb listener. The user interface, Couchdb and the language servers are, in terms of the Purescript program, just coroutines. This model is complemented with ‘transformers’ and ‘consumers’. So on a high level of abstraction the core is a process consisting of producer and consumer coroutines.
PEP: Perspectives Exchange Protocol
Distributed Runtimes exchange information using the Perspectives Exchange Protocol. A PDR will put an update directly into the Couchdb Post database of a recipient. Notice that, obviously, the IP address of this recipient must be known (and accessible) and that the sender must be allowed to move data into this database. This may seem very unsafe, but remember that the Core will check each and every individual update with the governing model. So, apart from protection we may add add at the Couchdb level, the integrity of data in the Bubble is fiercely protected.
What if the recipients node is not available? This may happen, as laptops and desktops usually are not online 24/7. Here we rely on the user having relations that he trusts with handling his post. Notice that this trust doesn’t have to run deep; a node that periodically receives updates for another user could try to piece together what contexts and roles these updates constitute, but this will be a haphazard process not unlike guessing the image to a jigsaw puzzle one has just a few pieces of. Anyway, these relations constitute a ‘post cluster’ and will accept updates (‘post’) when the user himself is offline. A PDR will try to reach a recipient first; if that fails, he’ll try nodes in the post cluster, one by one. This will be repeated with ever larger intervals until the post is delivered, somewhere.
Who should receive what updates? A PDR that has an update to send, consults the model. Each user role in a context with a perspective that includes an action that has the updated role or context or property as object, will receive the update. These are, by construction, all those who should have access.
Confidentiality, Integrity and Availability
The confidentiality of Perspectives data is regulated by the model. Indeed the central article of faith is that every user has access to all information necessary to perform his actions, automatically – and nothing more. Above we’ve given an impression of how this comes about. The model – the access rules, if one wishes – is consulted on each and every modification of data, by all PDRs involved.
Integrity is about the veracity of a representation. Is it complete? Is it’s semantical relation to the represented part of the world correct? The Perspectives philosophy is relevant to this last question. We state that if those who get to see a representation, exclusively are those involved in the context it is used in, information will most likely be interpreted correctly. Indeed, this is the most important cornerstone of the philosophy. Moreover, because we raise sharing information to the highest level possible, thereby preventing double entry and ensuring that information is entered by users who have a stake in its correctness, we achieve the highest possible reliability of the source. Thus, tying source and destination of a communication process into the same context, we claim that data integrity is maximal.
Availability is about lost or otherwise inaccessible data. Perspectives uses redundancy to mitigate the risk of loss. A user can configure a second cluster of peers to duplicate his data to. This automatic backup system rests on Couchdb’s famous replication protocol. Again, a user should choose peers he trusts with his data. Notice that here these peers will have a complete copy of, in principle, confidential data. Professional backup partners may spring up, offering paid and trusted services to this end.
React library
To support a programmer creating custom screens, we supply a library with a number of components. Conceptually, we use the distinction between container components and presentational components (see Dan Abramovs’ article here). The library contains just container components. Using these components, the programmer navigates, for a particular user, to a context and then to roles and a view on such a role. The view functions as a data source to his React program. He can connect presentational components to them, either self-defined or standard components taken from a library to his liking.
The library hides the connection to the PDR Core. A number of functions is available that allows him to handle a user event, such as checking a box. Such a function will lead to an update on the Core, persisted in the local Couchdb, distributed to all those involved who should be in the know. All this is completely automatic.
Repository
The repository is a website built using standard techniques. It offers a Graphical User Interface for humans searching for a model that suits their needs (discovery). It also offers a programmatic interface for the PDR. This is because when a user receives an invitation to take a role in a context, he needs the model to do so. So on opening the invitation, the PDR downloads the model from the repository.
This is conceptually simple. It gets more complicated because the user operating the PDR must have paid for the model – either by subscription or for that particular model. The repository sends a payment proof with the model. In the case of a subscription, that proof must be updated monthly.
Obviously, if a user subscribes to a repository, he will have to pay. So the repository must handle this, too. Programmatically the implementor of a repository can choose to create a standalone program that uses the PEP, or can operate a PDR to handle the traffic.