· 7 min read
The road to ActivityPods 2.0
Since this summer, thanks to a grant from NLnet as part of the NGI0 Entrust Fund program, we've been working hard on ActivityPods 2.0, with a planned release in spring 2024. The aim of this article is to explain this background work and what's at stake.
As a preamble, we feel it’s important to point out that, when we developed the first version of ActivityPods, we had to make choices that we knew would not be scalable or secure. But our aim at the time was to release working applications in a relatively short time. Indeed, the version 1.0 was developed at the end of 2021 over a period of 3 (unpaid) months, and our finances didn’t allow us, at the time, to do more.
This strategy was a success: in the Compiègne area (north of Paris), the apps Welcome to my Place and Mutual-Aid are now used by a community of around 500 people, each of whom has a Pod - sometimes without even knowing it! Over the past 2 years, performance has always been good, there have only been a few bugs, and above all, it has enabled us to test the architecture with a concrete case.
Nevertheless, there are still a few fundamental problems that need to be addressed, as we shall now see.
External applications backend
In v1, ActivityPods applications are frontend-only, and rely on backend code residing in the Pod host (i.e. in the main ActivityPods repository) to properly manage certain “side effects” that cannot be handled by the frontend.
For example, when an event on Welcome to my Place reaches the maximum number of participants, it is marked as “closed” and invitees can no longer register. Another example: for an Open Badges POC we developed for the ActivityBadges project, Open Badges can only be generated on the backend. For the time being, it’s up to the Pod host to manage this.
This solution is clearly not scalable, since whenever a new application is deployed (or updated), it is imperative that all Pod hosts update ActivityPods. This implies a level of coordination and trust that may be possible in a small experimental area, but is unthinkable on a larger scale.
In v2, all applications will have a their own backend where their specific code will be run. This backend will include at least an ActivityPub actor for the application, which will be able to communicate with users’ Pods (also ActivityPub actors).
We’re going to use part of Solid application interoperability spec to allow applications to register with the Pod and declare their access needs. For the time being, application registration will go through ActivityPub, as this is the simplest with our current architecture. We’re waiting for this spec to be completed before we fully comply with it.
The backend will be able to listen in and react to what’s happening on the Pod. Via Solid notifications and websocket channels, it will be able to listen to the user’s ActivityPub inbox and outbox. When the user creates or updates data on his or her Pod, this will be indicated in the activity stream of his or her outbox to facilitate this listening.
Finally, the backend will be able to send notifications to the user, when certain activities are received (e.g. an invitation to an event). This notification will simply be a Note sent via ActivityPub to the user, which will be transformed into an email or web push notification.
More flexible containers and collections
In v1, LDP containers could not be created by applications or users: the Pod host had to declare the containers. This is a similar problem to the previous one, which was due to the overly rigid architecture of SemApps, the semantic web toolbox on which ActivityPods is based.
The problem was fixed this autumn on SemApps side, which is now able to detect containers “on the fly” according to the data present in the triple store. This will enable more flexible management of LDP containers, which can be dynamically created as required.
For the time being, the plan is for applications to declare the type of resource they wish to use (read and/or write) when registering. This will automatically create a corresponding container, if one does not already exist, and the appropriate permissions will be given to the applications concerned. Users will be asked to give their consent via a dedicated screen.
With regard to ActivityStreams collections, which are widely used by ActivityPub and which we find very useful for indexing data, the problem is similar: in v1, applications can declare “custom” collections that will automatically attach themselves to certain types of resources, but the declaration is made in the Pod host thanks to an internal service.
The solution currently envisaged is to develop an API enabling (now external) applications to create collections themselves and add or remove items from them. This API does not exist in the ActivityPub specification, but there are proposals along these lines, and we intend to contribute to them.
App-specific tokens and more secure authentication
The final major drawback of v1 concerns security. Every token currently issued gives access to all user data, both read and write. If a malicious application (or hacker) steals this token, it can take control of the user’s Pod, and potentially wipe out everything on it.
We were also dissatisfied with the non-standard and insecure way in which authentication was handled. The JWT tokens we were generating worked well in a simple backend-frontend architecture, but the security for a multi-application architecture was insufficient.
So this autumn, we took the time to study the OAuth 2.0 and OIDC standards, thanks in part to an excellent online course. This enabled us to gain a better understanding of the Solid-OIDC specification, which we had previously ignored for lack of available brain time.
Armed with this understanding, we implemented the first part of the Solid-OIDC spec, which concerns authentication. For this, we used the node-oidc-provider library, also used by the Community Solid Server.
What’s still missing is the use of the DPoP protocol for resources requests. In the meantime, we’re using the ID token returned by the server. This is bad practice, but the advantage is that it contains both the user’s and the application’s WebID, thus allowing to limit access to the data that the application is authorized to manipulate (to request resources from a remote server, we always have to use a proxy endpoint).
Time and resources permitting, we will finalize the implementation of Solid-OIDC for the release of v2 (or shortly thereafter), which would enable any Solid application to connect to ActivityPods. The other advantage is that SemApps front-end components would become Solid-compatible, opening up their use to the entire Solid community!
Bonus: Mastopod
The NLnet team has agreed to include in the funding a development we’ve been wanting to make for a long time: an application compatible with all Mastodon instances, but which will have the particularity of hosting data on a Pod. This future app has a perfect name: Mastopod.
We think Mastopod will generate a lot of interest from the ActivityPub community. Developers will realize that, with the help of the ActivityPods framework, it’s super easy to create ActivityPub-compatible applications, since all the basic functionality (inbox, outbox, authentication, etc.) is already handled by the backend. All that’s left is to take care of the application-specific aspects.
And above all, users won’t have to recreate a profile and find again their contacts every time they use a new app, as is currently the case (they need an account for Mastodon, an account for Peertube, an account for Pixelfed…). Thanks to the Pod architecture, they will be able to immediately find all their data and contacts!