This release features 'Chat as system functionality'
MyContexts Version v0.24.0 delivers the following milestone in the project Perspectives: Making Models as supported by NLnet:
This has been described as follows: Chat could (and indeed has been, up to some extent) modeled within the Perspectives Modeling Language, but we deem it to be so essential that we will include it in the system functionality itself, so user roles sharing a context can always add chat utterances to that context.
I thought it would be a good idea to have Chat under model control. It has now been implemented on the basis of the following design decisions:
As a consequence, we can use all the power of perspectives to model what user types have access to a particular chat. We can even include a read-only participant! A context may have zero, one or multiple chats. A chat may include one or many participants.
To provide a pleasing user interface, I have built a widget on the chatscope toolkit. I have added audio recording to the widget.
Including media files in the chat proved to be quite a challenge for a distributed system. Or, at least for the PDR. It uses RabbitMQ to exchange transactions of deltas to keep our peers informed of the data-changes we've made. However, RabbitMQ provides but a narrow channel suitable just for relatively small JSON files. Even if we could send a (large) media file through that channel, the RabbitMQ server doesn't provide enough storage capacity to keep a reasonable number of media files in transit in. Remember that a peer may not be online at the moment we share a media file!
Instead, I've realised the sharing functionality by treating media files as a combination of claim data - that is sent through RabbitMQ - and files in a cloud storage. A peer receives claim data and 'claims' the media file from the cloud storage. This makes for privacy by obscurity.
But what cloud? Again, we cannot provide some central server with a colossal amount of memory. Instead, each user should have her own private cloud to put the files in that she wants to share - requiring, of course, that her peers have read-access to those files. This fits nicely with the idea of private cloud data, that may be shared by generated urls. Since only the owner has access to a directory listing, others will have a hard time indeed to guess file names and retrieve them from a persons storage without her sharing the links.
In an ideal world, MyContexts would support all major cloud storage providers. Currently, we've picked one: Mega. It is not one of the biggest players, but has a very decent free tier and takes privacy seriously.
Now for a new user of MyContexts, having to set up a cloud service just to be able to try sharing a file may be a big requirement. I have gone out of my way to create a courtesy cloud store that allows a new user to upload a limited number of files (currently set at 10). On exceeding that limit, he will see a message explaining the situation. It holds a clickable reference to another app in MyContexts, that can be used to set up his own private cloud. The app links to the Mega website.
Modelling this proved to be an interesting challenge both conceptually as well as technically. Almost all relevant mechanisms in Perspectives are required to make it work. As short presentation: the Perspectives project has signed up with Mega, reserving a store of 20 Gb for new users. Conceptually, the problem is that in order to store a file in that store, an installation needs the credentials to sign in. However, clearly Mega would be displeased if we just threw the credentials to one of their accounts out into the world by including them in our source code or a model. To solve this problem, I built a small service that holds the credentials. A new user doesn't approach the Mega cloud directly, but through the service. This somewhat ameliorates the situation, but now we have a service that anyone can find rather easily and then just spam full with nonsense files. To mitigate that risk, I require a secret key to upload a file via the service. Now the problem has been deferred to handing out the keys. Again, how do we do that in a distributed service? The solution makes use of the notion of trusted network. In short: anyone inside the network is trusted and thus we allow him to request a key at the service and hand it over to a new user. After acquiring a key, the new user is inside the trusted network, too. The final piece of the puzzle is that upon getting into contact with a new peer, the receiving peer checks whether this new contact has such a key. If not, he requests one at the service and hands it over to his new contact. All this happens automatically. It critically depends on executing actions on state change. It also required refinement of perspectives on properties (see below).
The video below demonstrates this form of Chat. It shows MyContexts as it runs on a MacBook mid 2015, 2,5 GHz quad core, 16 Mb internal memory. The current version has been shown to run on macOS Monterey 12.7.6 in Chrome and in FireFox.
chat
instruction, that designates a particular role as the source of a Chat Widget in the graphical user interface. It depends on two keywords (messages
and media
) to map two String-valued properties to the inputs of the Chat Widget.action
(triggered by the end user) or in a state change
(entry or exit). Any statement is always tied to a particular user role. The compiler now checks if the perspectives given to the user role allow the changes specified by the statement. This is important, because if the deltas resulting from the statement execution are shared with others to synchronise their installation, it will check whether the user role taken on by the peer from which the delta originates, did have a sufficient perspective. In other words: before these compiler checks it would happen that a change was made illegally by peer A and peer B refused to carry out the same change.A selection from the 66 commits that have been made on the PDR since the previous release.
contextinstance
and roleinstance
unary operators to construct an instance from a string calculated by a query. These keywords may be applied to a perspective, or to a property.filter
keyword.