Issue: https://github.com/solid/specification/issues/525 Room: https://meet.jit.si/solid-cg Datetime: 2023-08-08 13:00 UTC [WorldClock](https://www.timeanddate.com/worldclock/fixedtime.html?msg=Proposed+Solid+CG+Meeting+on+%22Alternative%20solutions%20to%20container%20HATEOAS%22&iso=20230808T13&p1=1440&ah=2) ## Context Let us agree that Hypermedia As The Engine Of Application State (HATEOAS) is an indispensible feature of the Web. In the context of a data store, that not only implies that a `GET` request on a resource should return all the hypermedia affordances on a resource but also that a user should be able through a `PUT`/`POST`/`PATCH` request create any arbitrary hypermedia affordance on the resource. Now, consider the situation with Solid Containers: + A `POST` request (with slug header) creates a resource in the container which is reflected by creating an `ldp:contains` triple in the representation. + A `PUT` (and `PATCH`) request is not prohibited. Though, as I shall demonstrate, it is unclear whether the request can be used to create RDF reesources only or even non-RDF resource. In my specific case, I want to support transclusions. The simplest way I can think of doing this is to store relations to external resources as peers to contained resources (a bit like symlinks but spanning the entire web) and statements explaining what the contained resources and linked resources are about (see my [Car Example](https://stackblitz.com/edit/js-na8gnq?file=data%2Fcar%2Findex.ttl) on stackblitz). Realize that I need the contained resources as well because of cascading permissions (and not have broken links in my own domain), so I cannot just make them as linked resources. But in other cases this representation can be just about anything - html, img etc. But now what should happen when I make a `GET` request on a ~~resource~~ container: 1. Return containment triples (and metadata) T? 2. Return a representation R previously PUT on the ~~resource~~ container? 3. Return a combination of the two; since only that represents all the hypermedia affordances on a resource. In which case how should this combination be done, especially if the representation R is non-rdf? As this behaviour is undefined in the Solid spec, each implementation is doing their own thing. Pavlik captures [CSS decisions](https://github.com/solid/specification/issues/525#issuecomment-1602514678) in the issue thread! Next, how do you update the resource based on the representation you have received previously? * There is really only one restriction in the Solid spec, thatone does not overwrite containment triples T. * Answering this is especially vexed if, as per previous suggestion 3 for GET. (Issue #198), we returned a mixed state previously. How complex do you want clients to be? eg should the client identify containment triples, say in a complex format like HTML+RDFa and update the HTML and put the RDF back? At some point IMHO this gets absurd. See !!permathread!! #198 * Again CSS does something funny in only allowing PATCH --- ## Proposed Solutions ### Features + Containment Statements T and client uploaded representation R should be separate. + One should be discoverable from the other. + or Clients should be able to GET both together, if possible as all the hypermedia affordances should be exposed for a resource (adjusted for media type and after all instance manipulations obviously) in a GET request. Logically putting R or T in a link relation is a hack as both describe hypermedia of the container proper. + But then you should be able to PUT both tohether which is a contradiction as you create containers using POST only. POST PUT/PATCH HTTP/1.1 GET example.org /Car/Steering/ 200 OK Link: </.meta/Car/Steering/>; rel="http://www.w3.org/ns/solid/terms#TBDserverManaged" ```ttl </Car/Steering/> ldp:contains </Car/Steering/horn> ``` </.meta/Car/Steering/horn/> /containedStuff; rel="http://www.w3.org/ns/ldp#PreferMembership" ```http HTTP/1.1 PUT example.org /Car/Steering/ ``` ```ttl <> rdfs:label "My car steering" . <Horn> rdfs:label "HONK" ``` </.meta/*/**> only GET, server does AuthZ </.acl/Car/Steering>; rel="acl" </Car/Steering/.acl> </Car/Steering/> ldp:contains </Car/Steering/.acl> ### Proposed Solution N-1 For `GET Foo/` * Return R * Return link relation to T Question: how do you link stuff in R with T? Say you delete a container, a triple `t` in T, what is your change in R for statements pertaining to `t`. ### Solution N * include parameter on `Prefer` header. ### Proposed Solution N+1 How about we return a multipart/mixed response with both the representation R and containment triples T in a multipart response. Omit one or the other parts based on the Prefer > return > include header. ### Proposed Solution N+2 (3/10/23): How about we content-negotiate on the container URL say `foo/` and use the `Content-Location` header to redirect the user to another resource. Link relations will always exist. Use the accept and prefer header to request client RDF. Cons: + This will confuse browsers as `Accept=*/*` will redirect typically to Container RDF. A way around this is to examine the user agent and if that is a browser emit HTML (or another non-RDF resource), otherwise emit Container RDF. + `index.*` (or another chosen by the server) namespaces cannot be used. ```HTTP # Returns Container Triples GET foo/ HTTP/1.1 Host: www.example.com Accept: */* HTTP/1.1 200 OK Location: foo/ Link: <http://www.w3.org/ns/ldp#BasicContainer>; rel="type", <http://www.w3.org/ns/ldp#Resource>; rel="type" <index.html>; rel="clientManaged" # or <index.ttl>; rel="clientManaged" Content-Type: text/turtle <containment triples> # Returns Client Triples(1) ## Is responding to "include" parameter like this legal? ## Also maybe breaks RDF covertibility unless we use `index` (or similar as convention). GET foo/ HTTP/1.1 Host: www.example.com Accept: text/turtle Prefer: return=representation; include="http://www.w3.org/ns/ldp#PreferMinimalContainer" HTTP/1.1 200 OK Location: foo/ Content-Location: index.ttl #Or another Convention Link: <http://www.w3.org/ns/ldp#BasicContainer>; rel="type", <http://www.w3.org/ns/ldp#Resource>; rel="type" Content-Type: text/turtle Preference-Applied: return=representation <containment triples> # Returns Client Triples(2) ## Can we directly specify `Content-Location` in the request instead? GET foo/ HTTP/1.1 Host: www.example.com Accept: text/turtle Content-Type: index.ttl HTTP/1.1 200 OK Location: foo/ Content-Location: index.ttl Link: <http://www.w3.org/ns/ldp#BasicContainer>; rel="type", <http://www.w3.org/ns/ldp#Resource>; rel="type" <index.ttl>; rel="clientManaged" Content-Type: text/turtle <containment triples> # Returns Client Triples(3) ## Common RDF triples; does not map well to the FileSystem GET foo/ HTTP/1.1 Host: www.example.com Accept: text/turtle Prefer: return=representation; include="http://www.w3.org/ns/ldp#PreferMinimalContainer" HTTP/1.1 200 OK Location: foo/ # Content-Location: index.ttl ## All RDF triples are stored at /foo Link: <http://www.w3.org/ns/ldp#BasicContainer>; rel="type", <http://www.w3.org/ns/ldp#Resource>; rel="type" Content-Type: text/turtle Preference-Applied: return=representation <containment triples> # Returns HTML on Browser (Cannot rock the boat) GET foo/ HTTP/1.1 Host: www.example.com Accept: */* User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 HTTP/1.1 200 OK Location: foo/ Content-Location: index.html Link: <http://www.w3.org/ns/ldp#BasicContainer>; rel="type", <http://www.w3.org/ns/ldp#Resource>; rel="type" Content-Type: text/html <index.html> # Force Return Container Triples on Browser GET foo/ HTTP/1.1 Host: www.example.com Accept: text/turtle # Alternatively we can make `Prefer: return=representation; include=...` mandatory User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 HTTP/1.1 200 OK Location: foo/ Link: <http://www.w3.org/ns/ldp#BasicContainer>; rel="type", <http://www.w3.org/ns/ldp#Resource>; rel="type" Content-Type: text/html <containment triples> ``` Resources such as `foo/resource` can only be created by POSTing or PUTing from `foo/`. Otherwise location relationships will not be recorded. However PUT can be used to replace the resource `foo/resource` provided it has the same `Accept-*` parameters). ### Storage Operations to Access Modes mapping source: https://github.com/solid/web-access-control-spec/issues/85#issuecomment-913456115 Request | Operation | Access Modes | Level -- | -- | -- | -- GET /C/R | Read | acl:Read | Resource HEAD /C/R | Read | acl:Read | Resource OPTIONS /C/R | Read | acl:Read | Resource POST /C/ | Create + Update | acl:Append [1] | Resource, Content PUT /C/R + If-None-Match | Create, Read | acl:Write, acl:Read [2] | Resource PATCH /C/R + If-None-Match + INSERT | Create, Read | acl:Write, acl:Read [2] | Resource, Content POST /C/R | Update | acl:Append (or acl:Write) | Content PUT /C/R | Create?, Update | acl:Write [2][3] | Resource PATCH /C/R + INSERT | Create?, Update | acl:Write [2][3] | Resource?, Content PATCH /C/R + DELETE + INSERT | Create?, Read, Update | acl:Write, acl:Read [2][3] | Resource?, Content [4] PATCH /C/R + DELETE | Update, Read | acl:Write, acl:Read | Content DELETE /C/ | Delete | acl:Write, acl:Read | Resource DELETE /C/R | Delete | acl:Write [5] | Resource 1. POST /C/ is to "perform resource-specific processing on the request payload" (RFC 7231). The semantics in Solid Protocol: server-assigned name for resource to be created as member of container, and to update containment statements. So, Create operation of /C/R and an Update operation on /C/. 2. Requires Append (or Write) on /C/ and Write on /C/R to Create. 3. Requires Write on /C/R to Update. 4. PATCH /C/R + DELETE + INSERT could be interpreted as a specific content-level update (or modify) operation where DELETE s1p1o1 and INSERT s1p1o2 - I don't know what the practice/terminology is out there for this. We can come back to this later. 5. Requires Write on /C/ and /C/R. ### Decision Tree TODO: incorporate different representations, like homepage case below * Do we want to allow client describing containers * No - Limitations: * Very unweb-like: Cannot host HTML pages at a nice address like `example.com` or `example.com/foo -> example.com/foo/` * Cannot describe (provide additional triples about) the RDF resource (such as how it relates to container resource) in the container. * Yes - Do we create one resource for all RDF or separate client and server managed components of the container in distinct resources * One: document Prefer + include, timestamp propagation issue, limited to RDFSource? * Separate: Which resource is denoted by container IRI and which is related (auxiliary or otherwise) * Server managed a primary * Client managed a primary Do we think of client managed triples as augmenting Container RDF or more similar to Non RDF representations (as a thing that is logically distinct from container triples)? ### Constraints imposed due to resources metadata Timestamps can trigger recalculation for all the resources up to the root. It can be avoided by including timestamps in link headers or auxiliary resource. ### Processing representation on PUT Servers treats PUT as a simple replacement of state [RFC9110]. These state replacement is supposed to be (i.e. SHOULD, not MUST) only for client manages state updates update. 5.2.4.1 LDP servers **should not** allow HTTP PUT to update a LDPC’s containment triples; if the server receives such a request, it should respond with a 409 (Conflict) status code. What about idempotency requirements? Since PUT overwrites state completely: + Are you even allowed to PUT something that does not contain the containment triples (which is still part of the resource state)? + Is putting a non-RDF resource the equivalent to changing the resource from a RDF to non-RDF and deleting all containment relationships? ### Homepage Usecase Users may want to have HTML representation of the homepage, which as storage root (container) has the following constraints: + At the domain root example.com == example.com/ [URL spec] + We cannot expect a browser user agent going to example.com to get anything other than HTML as response. Therefore with nothing else specified (`Accept: */*` and no `Prefer`) in the headers and HTML as the only representation PUT in the container: + We need to return HTML + To continue to support most widely published practice on the Web. + Do we include RDFa with container triples in the HTML? This breaks the PUT -> GET 1:1 relationship + If it breaks, does it not similarly break Turtle/JSON-LD with PUT? The broader point here is about processing / validating the payload. + Do we provide a link relation to container triples as an auxiliary resource + Or, we need to redirect to HTML + It follows that same rule will then have to apply to any route that ends with a trailing slash (this incidentally has the effect of normalizing all container routes with domain route) #### PATCH Constraints 5.2.7.1 LDP servers are **recommended** to support HTTP PATCH as the preferred method for updating a LDPC's minimal-container triples. I suppose we do not want to use PATCH to update containment triples but nothing says that in the LDP spec? ### POST LDP Primer uses POST both to create LDP-RS and LDP-NR (non RDF) resources. There are insufficiant examples of LDP-BC. ### Address Book Example ### TODO * [ ] note on using NonRDF-Sources describing container * [ ] note on using HTML-like resources (ie resources that can embed RDF) describing the container * [ ] record agreement of need for client managed description for the container