Collaborative Multi-Tenancy

Multi-tenancy is kind of boring. Isolate your customers. Ensure a fair distribution of resources among them. Done.

But when we add the word collaborative, everything changes. Bootstrap trust between strangers. Help them to communicate and share. The success of many apps depends on getting this right.

How to bootstrap trust? Repeatability and control. With repeatability, we can link interactions over time to build a reputation. With control, we create a safe environment that encourages risk-taking.

Caf.js provides repeatibility and control for your app with a Trusted Bus. A Trusted Bus mediates interactions between Cloud Assistants (CAs), authenticating requests, and enforcing your access control policy.

Caf.js implements high level communication services on top of the Trusted Bus. For example, to support a publish-subscribe model, or replicate data with Distributed Data Structures (DDS).

CAs access communication services with transactional plugins. They see replicated data as read-only, internal state. This leads to a simpler programming model, which recovers from failures transparently. See the Reliable Service Orchestration section for details.

Let's describe two use cases, and then we dig into the details.


Worldwide coordination of device actions

Warning: do not use Caf.js for safety critical applications.

Bring your own gadget. Register it with a Caf.js app to create a reverse service worker (see the Permanent Presence section). Decide who can access it. Choose the tags your device subscribes to. Every time one of your friends posts an action using one of these tags your gadget will respond.

And here comes the fun part. Gadgets can use Timed Bundles to start actions based on Coordinated Universal Time (UTC), not just when the action arrives. An action could also contain a sequence of commands, spaced in time. And the Caf.js client library helps gadgets to synchronize clocks with the Cloud.

This means that, given a second or two for propagation, we can synchronize actions, within a hundred milliseconds of each other, on hundreds of thousands of devices across the world. And soon it will be millions.

Moreover, not all posts are created by humans. See the Autonomous Compute section to learn how to inject posts with continuous queries.

And not all endpoints are gadgets. A reverse service worker can also represent other app instances or services. Coordination is not only for the physical world.

The TweetIoT (watch video) app puts all these ideas to practice. Hashtags in posts trigger gadget actions. Delegation simplifies access control policies based on groups. The remarkable part is that the cloud backend is only 154 lines of JavaScript.

Implementing the WAB (Web App Background) Architecture

The WAB Architecture creates collaborative apps in the browser with two independent layers. The top layer is transparent, and contains video/audio components (WebRTC). The background layer is a Web App, synchronized with Caf.js, and running everywhere. The merging of these layers is at the endpoints not at the source.

This architecture has many advantages (watch video), for example, personalizing content, or sharing local bluetooth devices with other participants in the call. And after sharing, device actions can be synchronized not only with global time, but also with the video stream.

Your next Zoom party will never be the same.

The HelloWAB (watch video) app implements the WAB architecture using Daily video APIs.


How are these experiences implemented with Caf.js?

Trusted Bus

CAs authenticate requests before injecting them in the Trusted Bus. Since a CA name contains the name of the CA owner, the true owner of each request is always known.

When a request reaches a bus endpoint, a check ensures that the request owner is allowed to invoke the request method. A request method can also change behavior depending on who calls it.

Decentralized authorization systems, such as SDSI, inspired our access control policies. Each owner maintains a local namespace, mapping names to principals. When several principals have the same local name, they form a group. Policy associates methods with allowed principals, or groups of allowed principals.

Names can also refer to local names in other namespaces, delegating the task of maintaining group membership to other owners. These owners could further delegate to other owners. However, trust is not a transitive property, as Facebook users learned the hard way, and delegation chains tend to be short.

CAs use SharedMaps, described next, to represent groups. Group updates propagate in milliseconds. The security library handles link traversal between SharedMaps to implement delegation.

SharedMap

A SharedMap is a single writer-multiple reader DDS that can efficiently replicate data across many CAs. CAs see this data as internal state. The writer CA is the owner, providing a scoped name for the SharedMap.

CAs blend with SharedMaps in a novel way. Adding a SharedMap does not change the semantics of the Actor Model, and it can always be replaced by extra requests. This means that we never introduce deadlocks or data races. The application just scales better and with less memory.

SharedMaps can be replicated in the Cloud, in devices, or in the browser. They can also contain serialized methods. They are updated with a transactional plugin, ensuring that changes to both code and data are atomic, and only visible when they respect serialization.

A SharedMap provides a safe mechanism to update code and data everywhere.

Publish-Subscribe

Our implementation supports two kinds of topics:

  1. Private: similar to a blog. There is only one CA that can publish messages, but anybody can subscribe to receive them.
  2. Forum: anybody can publish or subscribe, but subscribers can filter publishers by setting Trusted Bus policies.

SharedMap and Publish-Subscribe complement each other well. The former provides cheap, silent updates to many CAs. The latter triggers an action on each subscribed CA, and this is more expensive, but needed for external interaction.