Open RaggedStaff opened 6 months ago
I'm keen to get @balessan & the Startin'Blox crew's take on this: will this help you to align more closely with the DFC Standard, or move us further apart ? I'm hoping the former, as you already support Solid-OIDC & that includes WebID, right :question:
I'm also a little wary as I don't think solid:publicTypeIndex
is a well supported feature currently. For example ActivityPods doesn't have a clear roadmap to implement TypeIndexes, and the issue hasn't been updated since Oct '23. :worried:
@lecoqlibre - Could you propose an alternative to using I see you're proposing we have LDP containers defined with the DFC Standard as an alternative to solid:publicTypeIndex
that is more widely supported ?solid:publicTypeIndex
@lecoqlibre Could you clarify where we got to on using this alongside OIDC, as part of the current implementation ?
Would we only need platforms to implement WebId :question:
The following is a non-normative proposal as a first step.
Ping @RaggedStaff @simonLouvet.
The main idea of this proposal is to find instances of DFC Workspace
from a WebId. The workspace is a new concept we would add in our ontology. It describes a DFC environment and leads to the user's data. The DFC Workspace
is hence the main concept to discover data from for both Solid and non-Solid apps. For non-Solid apps like most of the current DFC platforms the workspace will lead to API endpoints instead of resource documents. In that case the authentication token is used by the platform to provide only the data of the authenticated user.
The concept of DFC Workspace
groups consistent DFC data together in a LDP container. It contains a single entry-point resource, let's say index
which describes the workspace concept and lead to user's data thanks to a TypeIndex
. Users might have several DFC workspaces especially when using Solid storages but non-Solid platforms can provide just one.
TypeIndex
are part of this proposal because they already offer what we need: a way to discover resources of a certain type. Other mechanisms could be designed but TypeIndex
are very simple and powerful. They are also widely used by the Solid community for a long time.
WebId of user and WebId of platform would be quite similar. This has the advantage of having the same code (especially in the connector) to discover data regardless if the WebId is about a user or a platform.
Note: non-Solid DFC platforms just have to implement a WebId for the platform. There is no need to implement a WebId for users at this time.
So a user or platform data discovery would work the same starting from dereferencing the WebId:
WebId profile
leads to the main TypeIndex
(public or private);TypeIndex
lists the available DFC Workspace
(s);Workspace
describes the DFC environment and leads to the TypeIndex
of the workspace;TypeIndex
of the workspace leads to the user's data.Note: all these resources could be returned into a single document.
Contains the WebId profile (https://platform.ex/profile/card#it) which is linked to a main TypeIndex
(public in the example).
{
"@graph": [
{
"@id": "https://platform.ex/profile/card",
"@type": "foaf:PersonalProfileDocument",
"foaf:primaryTopic": "https://platform.ex/profile/card#it"
},
{
"@id": "https://platform.ex/profile/card#it",
"@type": [ "foaf:Agent", "dfc:Platform" ],
"foaf:name": "Name of the platform",
"solid:publicTypeIndex": "https://platform.ex/publicTypeIndex"
}
]
}
Note: there were discussions about adding a foaf:ProfileDocument
to use instead of foaf:PersonalProfileDocument
where it make sense like in this case. It seems this predicate still does not exist. We might keep the existing predicate or use something else.
Leads to the DFC API entry point (Workspace LDP container) of the platform.
{
"@graph": [
{
"@id": "https://platform.ex/publicTypeIndex",
"@type": "solid:TypeIndex"
},
{
"@id": "https://platform.ex/publicTypeIndex#1",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc:Workspace",
"solid:instance": "https://platform.ex/dfc/"
}
]
}
Note: we could have a direct link to the index file instead of a link to the Workspace container so the name of the resource could be anything. While this can be done, it is preferable to normalize the Workspace entry point. This will ensure the Workspace is self-sufficient as it won't depend on external semantics (so potential breaking changes could be limited).
The dfc:Workspace
object is defined in the index
file at the root of the Workspace LDP container. It leads to a TypeIndex (dfc:hasTypeIndex
) which will list the data endpoints of the platform.
{
"@graph": [
{
"@id": "https://platform.ex/dfc/index",
"@type": "dfc:Workspace",
"dfc:hasTypeIndex": "https://platform.ex/dfc/typeIndex"
}
]
}
We could imagine to create other predicates for the Workspace like a name for instance.
Lists the DFC data endpoints of the platform. Here we discover that this platform provides a LDP container for dfc-b:Enterprise
, another one for dfc-b:Person
and also one for dfc-b:Catalog
. So if we want to create a new dfc-b:Catalog
for instance we know we should make a HTTP POST request to https://platform.ex/dfc/catalog/.
{
"@graph": [
{
"@id": "https://platform.ex/dfc/typeIndex",
"@type": "solid:TypeIndex"
},
{
"@id": "https://platform.ex/dfc/index#1",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc-b:Enterprise",
"solid:instanceContainer": "https://platform.ex/dfc/enterprise/"
},
{
"@id": "https://platform.ex/dfc/index#2",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc-b:Person",
"solid:instanceContainer": "https://platform.ex/dfc/person/"
},
{
"@id": "https://platform.ex/dfc/index#3",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc-b:Catalog",
"solid:instanceContainer": "https://platform.ex/dfc/catalog/"
}
]
}
Solid DFC applications will use user WebIds, provided by the Solid-OIDC authentication method. These WebIds should follow the Solid WebId profile specification.
DFC workspaces (dfc:Workspace
) should be discovered the same way as they are discovered for a DFC non-Solid platform.
In this proposal version the way that trusted platforms are stored is left to the platform. So the WebId of trusted/associated platforms could be stored by any mean by platforms.
Do we want platforms to expose their trusted platforms? If so we should add a predicate somewhere in the discoverability chain. The acl:trustedApp
might be used.
The platform Workspace LDP root container should contain every other Workspace containers and resources as child (ldp:contains
).
This is to ease future Solid compliance. This file tree also offers some advantages when exchanging a Workspace as the Workspace folder will contain every resources it needs (self-sufficient).
Note: this is not constraining how the sub hierarchy is structured. Any sub level with any path could be used as long as the paths are contained in the Workspace root container.
Here is an example of a Workspace root LDP container:
{
"@graph": [
{
"@id": "https://platform.ex/dfc/",
"@type": "ldp:BasicContainer",
"ldp:contains": [ "https://platform.ex/dfc/index", "https://platform.ex/dfc/catalog/", "https://platform.ex/dfc/some/child/path/enterprise/" ]
}
]
}
Thanks for putting this together @lecoqlibre ! I think this all seems to make general sense.
I'd like to comment on some details. Would it be worth moving this content to a .md file & start a PR ? Then we can comment on individual points more easily.
Also pinging @balessan @mkllnk for their thoughts... Will this make it easier for SiB to integrate with DFC ? Are there potential issues for OFN implementing this ?
Thank you. The points of compatibility and discoverability make sense. I don't understand the examples well enough though to completely understand. Could we use more realistic data in the examples?
From this, I guess that each platform will have one endpoint to create orders, for example. Unless we create a workspace for each enterprise to have its own order creation endpoint?
Is TypeRegistration an container of existing data, for example products? Or is it to create products?
@RaggedStaff I agree making a .md in a PR would be better. But I didn't know where to put that file. I think we should work on rewriting the standard and maybe move to ReSpec someday? Maybe for now we can put this into the "Technical specifications" section maybe in a sub section called "Data discovery"?
@mkllnk The current proposal assumes that non-Solid DFC platforms provide a single DFC workspace. It's an API endpoint which will serve for all users based on their email found into the OIDC token. But a non-Solid DFC platform can provide one different workspace URL per user. For instance, if the user "user@ex.org" is authenticated, the platform will have to retrieve his workspace from any mean it used to store it before and provide the link, let's say https:platform.ex/user/workspace/, in the main TypeIndex like:
{
"@graph": [
{
"@id": "https://platform.ex/privateTypeIndex",
"@type": "solid:TypeIndex"
},
{
"@id": "https://platform.ex/privateTypeIndex#1",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc:Workspace",
"solid:instance": "https:platform.ex/user/workspace/"
}
]
}
When using this kind of setup it is maybe better to use a private TypeIndex which should be discovered through the preferences file (see the TypeIndex spec). In fact we should maybe use only private TypeIndex as DFC data is supposed to be private.
The TypeRegistration
is an entry of a (TypeIndex
) index. In case of the TypeIndex of the Workspace, registrations are leading to LDP containers where a particular type of data can be found (ex: dfc-b:Catalog
or dfc-b:Enterprise
, etc). These containers are used to GET data but also to POST, PUT or PATCH. A platform could also provide resources (documents) instead of containers using the solid:instance
predicate.
I don't see how to provide more realistic data as only the "https://platform.ex" and "Name of the platform" should be respectively replaced by the real platform URL and its name.
Thank you, @lecoqlibre. That answers a lot. So we could have https://openfoodnetwork.org.au/api/dfc/index to display the private type index. That URL is referenced in a webid file? Where does that come from?
@mkllnk WebIds of platforms are just a simple document you put at some URL on the platform. When dereferenced the WebId gives information about the platform. A platform will have to store the WebIds of the platforms it interacts with. WebIds can be seen as an entry point to discover data from.
To use a main private TypeIndex we should pass through the pim:preferencesFile
to find the preferences file in which we will find a link to the private TypeIndex.
Below is a full example for OFN. Here is the WebId using a preferences file:
{
"@graph": [
{
"@id": "https://openfoodnetwork.org.au/profile/card",
"@type": "foaf:PersonalProfileDocument",
"foaf:primaryTopic": "https://openfoodnetwork.org.au/profile/card#it"
},
{
"@id": "https://openfoodnetwork.org.au/profile/card#it",
"@type": [ "foaf:Agent", "dfc:Platform" ],
"foaf:name": "Open Food Network",
"pim:preferencesFile": "https://openfoodnetwork.org.au/preferences"
}
]
}
The preferences file of the platform look like:
{
"@graph": [
{
"@id": "https://openfoodnetwork.org.au/preferences",
"@type": "space:ConfigurationFile",
},
{
"@id": "https://openfoodnetwork.org.au/profile/card#it",
"solid:privateTypeIndex": "https://openfoodnetwork.org.au/privateTypeIndex"
}
]
}
The private TypeIndex of the platform:
{
"@graph": [
{
"@id": "https://openfoodnetwork.org.au/privateTypeIndex",
"@type": "solid:TypeIndex"
},
{
"@id": "https://openfoodnetwork.org.au/privateTypeIndex#1",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc:Workspace",
"solid:instance": "https://openfoodnetwork.org.au/api/dfc/"
}
]
}
Then we could access to the workspace by dereferencing the index resource in the workspace container:
{
"@graph": [
{
"@id": "https://openfoodnetwork.org.au/api/dfc/index",
"@type": "dfc:Workspace",
"dfc:hasTypeIndex": "https://openfoodnetwork.org.au/api/dfc/typeIndex"
}
]
}
Following the value we got from the dfc:hasTypeIndex
predicate in the index, we can dereference the TypeIndex of the workspace:
{
"@graph": [
{
"@id": "https://openfoodnetwork.org.au/api/dfc/typeIndex",
"@type": "solid:TypeIndex"
},
{
"@id": "https://openfoodnetwork.org.au/api/dfc/typeIndex#1",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc-b:Enterprise",
"solid:instanceContainer": "https://openfoodnetwork.org.au/api/dfc/enterprise/"
},
{
"@id": "https://openfoodnetwork.org.au/api/dfc/typeIndex#2",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc-b:Person",
"solid:instanceContainer": "https://openfoodnetwork.org.au/api/dfc/person/"
},
{
"@id": "https://openfoodnetwork.org.au/api/dfc/typeIndex#3",
"@type": "solid:TypeRegistration",
"solid:forClass": "dfc-b:Catalog",
"solid:instanceContainer": "https://openfoodnetwork.org.au/api/dfc/catalog/"
}
]
}
At this point we know where to GET, POST, PUT, PATCH and DELETE data: we obtained the data endpoints of the platform.
Note that there are two type indexes: one of the platform used to discover the workspace (from the WebId); and one in the workspace to discover data endpoints.
Nice example, thank you!
My main question was aiming at knowing if there is a well known URL somewhere but it looks like the web id URL has to be stored somewhere to connect to that platform.
Within DFC, I guess that we could link straight to the type index of the workspace but the web id makes it compatible with other systems, right?
You mentioned the possibility of linking the web id to the id in OIDC. If we use a generic URL like https://openfoodnetwork.org.au/profile/card
then it doesn't identify the person. We still need a unique id per account, right?
My main question was aiming at knowing if there is a well known URL somewhere but it looks like the web id URL has to be stored somewhere to connect to that platform.
Yes currently the WebIds of interconnected platforms have to be stored. If we have some kind of a well-known we would also have to store the hostname of interconnected platforms. So anyhow we have to store something and I thought that storing the WebId was OK. Do you and the others think we should rely on the hostname of the platform instead? If so, several ideas come to my mind:
Within DFC, I guess that we could link straight to the type index of the workspace but the web id makes it compatible with other systems, right?
Right. But then you need a discovery mechanism. That's why we are proposing WebIds which would make our discovery mechanism compatible with the Solid ecosystem. This is a first step on the road to Solid compatibility.
You mentioned the possibility of linking the web id to the id in OIDC. If we use a generic URL like https://openfoodnetwork.org.au/profile/card then it doesn't identify the person. We still need a unique id per account, right?
In Solid, WebIds are used to authenticate and authorize users. A same WebId of a user is used across all the applications he uses. A WebId of a user is defined at one place only.
This proposal does not propose WebIds for users of non-Solid DFC platforms. This is not needed for now as Solid-OIDC is not the authentication mechanism of the DFC standard. This will be required when the DFC will be ready to get closer to a Solid compliance. The DFC platforms would then be able to use Solid PODs/Storages! But for now Solid and non-Solid DFC platforms are not compatible (they will need a compatibility layer at the authentication level).
https://openfoodnetwork.org.au/profile/card#it
I don't understand why you have to go through https://openfoodnetwork.org.au/privateTypeIndex to reach https://openfoodnetwork.org.au/api/dfc/typeIndex.
Why not reference https://openfoodnetwork.org.au/api/dfc/typeIndex in https://openfoodnetwork.org.au/profile/card#it?
Indeed, there are two type indexes:
I propose this distinction for several reasons:
Using the same discovery mechanism for Solid and non-Solid DFC platforms presents some advantages like:
Note: the private TypeIndex should be discovered through the preferences file and not directly from the WebId.
Here is a bit more details on DFC Solid apps: In a DFC Solid app, a user opens a workspace he want to work with. To do so he browses his storage(s) (or PODs) and select a DFC workspace (which is a LDP container). For instance, on one of his storages, he could have a workspace at /Documents/MyFarm/. When he opens it, the application can display the information contained in the workspace by following the links found in the TypeIndex of the workspace.
After speaking with @maxime, I understood that https://openfoodnetwork.org.au/privateTypeIndex refers to a user's data whereas https://openfoodnetwork.org.au/api/dfc/index was referring to the platform's data.
I also understood that https://openfoodnetwork.org.au/profile/card#it was referenced in the https://openfoodnetwork.org.au/preferences resource because it is the solid specification.
I don't understand where https://openfoodnetwork.org.au/api/dfc/index is referenced from the webId. is it this part?
"@type": "dfc:Workspace",
"dfc:hasTypeIndex": "https://platform.ex/dfc/typeIndex"
Overall, I agree with the specification proposed by maxime.
After speaking with @maxime, I understood that https://openfoodnetwork.org.au/privateTypeIndex refers to a user's data whereas https://openfoodnetwork.org.au/api/dfc/index was referring to the platform's data.
I would say it's the opposite :) The TypeIndex(s) of the platform, being public or private, must contain a link to a user's workspace. In a case of a non-Solid platform this workspace will likely be an endpoint which will provide data of the authenticated user.
I also understood that https://openfoodnetwork.org.au/profile/card#it was referenced in the https://openfoodnetwork.org.au/preferences resource because it is the solid specification.
I would say it's not only because of the solid specification. It's also a feature of the Semantic Web. The Web is made of documents (they could be stored in a file system or in a quadstore) and these documents can state different information about a thing. In the case of Solid preferences document, a new triple is asserted about the WebId and contain a link to private TypeIndex of this WebId owner (the platform). It can be called an "extended profile".
I don't understand where https://openfoodnetwork.org.au/api/dfc/index is referenced from the webId.
To discover the workspace endpoint of a non-Solid platform, one has to find a TypeIndex registration for the class dfc:Workspace
in the entire profile (public profile + eventually the extended profile) of the platform. In the example above, the platform is using a private TypeIndex. So to find the workspace endpoint, the client will have to follow its nose to find the private TypeIndex and then the TypeIndex registration for the class dfc:Workspace
.
To discover the workspace endpoint of a non-Solid platform, one has to find a TypeIndex registration for the class
dfc:Workspace
in the entire profile (public profile + eventually the extended profile) of the platform. In the example above, the platform is using a private TypeIndex. So to find the workspace endpoint, the client will have to follow its nose to find the private TypeIndex and then the TypeIndex registration for the classdfc:Workspace
.
there's always something I don't understand. https://openfoodnetwork.org.au/privateTypeIndex#1 mentions that it exists for the dfc:Workspace class and for the https://openfoodnetwork.org.au/api/dfc/ server, but how can it find the https://openfoodnetwork.org.au/api/dfc/index resource?
https://openfoodnetwork.org.au/privateTypeIndex#1 mentions that it exists for the dfc:Workspace class and for the https://openfoodnetwork.org.au/api/dfc/ server, but how can it find the https://openfoodnetwork.org.au/api/dfc/index resource?
The privateTypeIndex#1
currently references a ldp:Container
. The index
document is normalized, it's static like I previously said :
Note: we could have a direct link to the index document instead of a link to the Workspace container so the name of the resource could be anything. While this can be done, it is preferable to normalize the Workspace entry point. This will ensure the Workspace is self-sufficient as it won't depend on external semantics (so potential breaking changes could be limited).
This means that the index document is known by clients. Once they have found the workspace container they know they have to load the "index" document at the root of that container.
I'm open to discuss this point.
Maybe it would be better to reference directly a document within the container in the TypeIndex? This way the root document can be named anything. For instance the Solid chat client-to-client standard states that the root document should normally be "index":
Within that folder, the main channel is normally $ROOT/index#this
So I guess if the client does not find an index document it should search for an instance of a dfc:Workspace
at the root of the container.
This seems to be more flexible and should be as much efficient as a fixed document for most situations. It should only be less efficient when the root document will be different than "index" (as a search operation would be needed).
I think at the end I would be in favor of using a dynamic root document which should normally be "index".
Introducing WebID and profile
In the following proposal, any DFC user and platform would have their own WebID and profile(s). When dereferenced, a WebID would give information (profile) which can be used to find data.
The WebID could be used with Solid-OIDC for authentication and authorization instead of the email (currently used in the DFC token). This will be pursued in a future Major Version of the Standard
Platform WebID and profile
For instance, a public WebID of a platform (https://ofn.org/card#me) could look like the following and contain links to pieces of data:
If the platform want to restrict access to some information, it could move them in a private or restricted (ACL) preferences file:
User's WebID and profile
The following is an example of an user's WebID (https://webid.provider/user/card#me):
To restrict access to sensitive information, the user did move some information in the preferences file, protected by ACL:
Originally posted by @lecoqlibre in https://github.com/datafoodconsortium/standard/discussions/4#discussioncomment-7635937
Tasks