Solid compliance
Solid (Social Linked Data) is a set of specifications whose aim is to allow users to store all their data in Pods (“Personal Online Datastores”). Users have full control over their data and can give permissions to applications or people they trust.
ActivityPods supports fully the philosophy behind Solid, but it doesn’t yet support all the standards that have been defined for Solid. It is thus not Solid compliant. However, we are keeping a close eye on the development of this standard and are gradually moving towards it, with the aim of being fully Solid compliant.
Linked Data Platform
Each Solid Pod must comply with the Linked Data Platform protocol. This is a REST-based API for managing containers and resources.
Formats
We support both JSON-LD and Turtle format.
Containers
We support the BasicContainer
type, as required by the Solid specs.
Binaries
We support non-RDF resources (binaries) uploads. The description of the resource is stored in the triplestore as a http://semapps.org/ns/core#File
.
<https://mypod.store/alice/data/files/picture.jpg>
a <http://semapps.org/ns/core#File> ;
<http://semapps.org/ns/core#fileName> "picture.jpg" ;
<http://semapps.org/ns/core#localPath> "uploads/files/picture.jpg" ;
<http://semapps.org/ns/core#mimeType> "image/jpeg" .
There is an issue to use the Solid recommended metadata instead.
PATCH method
LDP resources and containers can be selectively modified using the HTTP PATCH
method. This method is important because it limits the risk of overwriting data, as with the simpler PUT
method.
However, due to the way RDF resources are formatted, the PATCH
method cannot be used like more regular REST-based APIs.
Many solutions have been proposed in the semantic web community to solve this problem:
Initially, the recommended method for Solid servers was SparqlPatch and this is what we implemented. In 2021, it was switched to a new N3 Patch method, as described in the Solid spec. It is not yet a W3C standard.
Until the N3 Patch method is standardized, we will continue to use the SparqlPatch format.
Example of a SparqlPatch update to modify a LDP resource.
PATCH /alice HTTP/1.1
Host: mypod.store
Content-Type: application/sparql-update
PREFIX as: <https://www.w3.org/ns/activitystreams#>
INSERT DATA { <https://mypod.store/alice> as:preferredUsername "Alice" . };
DELETE DATA { <https://mypod.store/alice> as:preferredUsername "ALICE" . };
Example of a SparqlPatch update to add or remove a resource from a LDP container.
PATCH /alice/data/events HTTP/1.1
Host: mypod.store
Content-Type: application/sparql-update
PREFIX ldp: <http://www.w3.org/ns/ldp#>
INSERT DATA { <http://mypod.store/alice/data/events> ldp:contains <http://url/of/resource/to/attach>. };
DELETE DATA { <http://mypod.store/alice/data/events> ldp:contains <http://url/of/resource/to/detach>. };
LDP Prefer header
👷 To be implemented. (#1168)
LDP paging
👷 To be implemented. (#176)
Identity
WebID
All ActivityPub actors are also WebIDs.
Click to see how an ActivityPods WebID looks like
{
"@context": "https://activitypods.org/context.json",
"id": "https://mypod.store/alice",
"type": ["foaf:Person", "Person"],
"dc:created": "2023-01-13T11:42:45.636Z",
"dc:modified": "2023-01-13T11:42:48.389Z",
"foaf:nick": "alice",
"preferredUsername": "alice",
"inbox": "https://mypod.store/alice/inbox",
"outbox": "https://mypod.store/alice/outbox",
"followers": "https://mypod.store/alice/followers",
"following": "https://mypod.store/alice/following",
"liked": "https://mypod.store/alice/liked",
"publicKey": {
"owner": "https://mypod.store/alice",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzlq6avqA4nwxGDqXMijZ\nixRIllyIDkF4rc9KxUHizysOJEBx9+WSfom0wlS1hPr+Qn90eb8h/8wjbvfXdjYm\npdusaTOrRMFEEMX4lquAPcgLxZKzQNAy8zUw/oBpnrep52Hmx7VfYFfCpAP7OxAL\nsA4mdKq1tx2e0g9so+5WeAa3yiKCvqkE6QJQXgk+WQ998aQg133HOUOqa1JKaU0k\nJK6fVYnJ74L7b6OWQDbehu0SBlAAypOJzkO4WysKOGiUCrrcgol2LmTImzCoTZS3\nPUUV0Rh2iI3v5t07vUfr5o415Em5YFwoPRhq3WH6hmP7wd9ducaFNl9Q/XyYQmPK\nG1F3RQNwtSy6aeAUajw6HTWd+FlFA6oA9LLaVxCoTxlejLB8IrYo3y2PGxPAXNl3\nqodsXWk/I/Jm/nus+RLVGCoJLl+i+zzEF0WG20+rfb0cNPjzuzgWuYY2PTBu78Ib\nizaXLv23fPLmxxUwHQgcCc4RgbmBAwhLWTsGETRLbDSWwBZnkv6PnMVlyrzs47jf\nr+vFYIxdiz8VvJe+sQ7hSrkzVjCluKIXhWT3bPtOkq4fepTnZmGi3qXRCg5cacd8\ni+KLaGtsHIsNyGOGfi55S5ZiSMo7kbeUxfM6xFjfhWuj3jDmMYfYQgBSmP2CNcEU\nTWmx7zJiaO3Me1q3sFG8Ti0CAwEAAQ==\n-----END PUBLIC KEY-----\n"
},
"endpoints": {
"void:sparqlEndpoint": "https://mypod.store/alice/sparql",
"proxyUrl": "https://mypod.store/alice/proxy"
},
"url": "https://mypod.store/alice/data/profiles/63c143b75ea9720d1b484a39"
}
Some Solid predicates are still missing (#122)
Authentication
Solid-OIDC
⚠️ Will be available in ActivityPods 2.0
In v2.0, we partly support authentication through Solid-OIDC. This is detailed in #121.
We do not support yet DPoP to request resources, but it will be implemented as soon as possible. In the meanwhile, the ActivityPub-defined Proxy endpoint should be used to request remote servers.
HTTP Signature
We support HTTP signature for server-to-server authentication. This is the standard used by ActivityPub.
There is now a proposal to integrate HTTP signature in the Solid protocol.
WebID-TLS
Considering WebID-TLS authentication mechanism, used before Solid-OIDC, is now only an option, we will not implement it.
Authorization
Web Access Control
We support the full WAC specification.
Access Control Policy
We do not support yet the ACP specification.
Application interoperability
⚠️ Will be available in ActivityPods 2.0
Solid Application Interoperability is a complex specification which is not yet complete. We’ve implemented everything we can at the moment and are waiting to see how it evolves before implementing the remaining parts.
We’re following the proposed ontology closely. On the other hand, application registration is done via ActivityPub, as this was the simplest method for us (applications are ActivityPub actors). We’ve also decided not to use ShapeTrees for the time being, as we don’t think they add anything to our triplestore-based architecture: instead, applications just declare the classes they want to manage.
What we implement
Applications declare their needs through
AccessNeedGroups
andAccessNeeds
, which are displayed on an authorization screen.- Before registration,
AccessGrants
andDataGrants
are created on the user’s Pod. The
ApplicationRegistration
resource is created on the user’s Pod and sent to the application through aCreate
activity.If the
ApplicationRegistration
is correct, it is saved on the application instance, as well as the corresponding grants. It returns anAccept
activity.Whenever the user connects again to the application, we check that the
AccessNeeds
haven’t changed. If needed, we show an authorization screen again andApplicationRegistration
is updated (with aUpdate
activity).If the user wants to uninstall the application, we delete the
ApplicationRegistration
(and corresponding grants) and send aDelete
activity to the application.
What we don’t implement yet
- Authorization flow is handled using ActivityPub. The application is an ActivityPub actor.
We only handle registration with
Applications
, not with otherSocialAgents
(we continue to use WAC permissions for the latter)- Pods don’t declare an
AuthorizationAgent
. It is handled internally. Pods don’t declare a
DataRegistry
. This registry is only used to list ShapeTrees. TypeIndex can be used instead.Applications don’t declare
AccessDescription
for the time being (we can deduce the accesses requested, and later it may be used to justify access to certain resources).Pods only create
AccessGrants
andDataGrants
, notAccessAuthorizations
andDataAuthorizations
. Grants can be shared with the registered application, while Authorizations cannot. The only difference between them is agrantedWith
predicate inAccessAuthorizations
that indicates which app was used to manage authorizations (but we manage that internally anyway).Pods don’t declare
RegistrySet
,ApplicationRegistry
,AuthorizationRegistry
as they are not visible from the outside. We just readApplicationRegistrations
,AccessGrants
andDataGrants
in their dedicated containers.
Type Indexes
👷 To be implemented (#1171)
Linked Data Notifications
Linked Data Notifications, standardized in 2017, were the basis of the ActivityPub protocol (standardized in 2018). LDN defines only inboxes, while ActivityPub defines a full communication protocol, with inboxes and outboxes.
Since we implement the full ActivityPub protocol, we haven’t needed to implement the more lightweight LDN protocol, but it could be added in the future.
Solid Notifications
⚠️ Will be available in ActivityPods 2.0
Thanks to the [Solid Notifications Protocol](https://solid.github.io/notifications/protocol, you can listen to LDP resources, LDP containers or ActivityStreams collections.
We support discovering the notification subscription services through the storage description resource. This can be found by doing a HEAD request on any resource in your pod and looking for the Link
header with the http://www.w3.org/ns/solid/terms#storageDescription
relationship.
This leads to the /.well-known/solid
endpoint which includes links to available subscription services.
{
"@context": { "notify":"http://www.w3.org/ns/solid/notifications#" },
"@id": "http://localhost:3000/.well-known/solid",
"@type":"http://www.w3.org/ns/pim/space#Storage",
"notify:subscription": [
"http://localhost:3000/.notifications/WebSocketChannel2023",
"http://localhost:3000/.notifications/WebhookChannel2023"
]
}
Webhooks subscription
To subscribe to the http://localhost:3000/foo resource using WebSockets, you use an authenticated POST request to send the following JSON-LD document to the server, at http://localhost:3000/.notifications/WebhookChannel2023/:
{
"@context": [ "https://www.w3.org/ns/solid/notification/v1" ],
"type": "http://www.w3.org/ns/solid/notifications#WebhookChannel2023",
"topic": "http://localhost:3000/foo",
"sendTo": "https://example.com/webhook"
}
The sendTo
predicate is the Webhook URL of your server, the URL to which you want the notifications to be sent.
Notification types
Just like the Community Solid Server, we support five different notification types that the client can receive. The format of the notification can slightly change depending on the type.
- Create: When the resource is created.
- Update: When the existing resource is changed.
- Delete: When the resource is deleted. Does not have a state field.
Additionally, when listening to a LDP container or an ActivityStreams collection, there are two extra notifications that are sent out when the contents of the container or collection change. For these notifications, the object
field references the resource that was added or removed, while the new target
field references the container or collection itself.
- Add: When a new resource is added to the container or collection.
- Remove: When a resource is removed from the container or collection.